/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.admin.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.*;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sida.voice.service.VoiceTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.cache.DictCache;
import org.springblade.common.config.ProjectApiConfiguration;
import org.springblade.common.constant.MaintenanceSmsConstant;
import org.springblade.common.constant.TemplateMsgConstant;
import org.springblade.common.constant.VoiceConstant;
import org.springblade.common.mq.MqMessageService;
import org.springblade.common.utils.message.MessageCenterUtils;
import org.springblade.common.utils.message.domain.MessageParam;
import org.springblade.common.utils.message.domain.SendRequest;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tenant.mp.TenantEntity;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.ObjectUtil;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.WebUtil;
import org.springblade.modules.admin.dto.PlanContentSaveDto;
import org.springblade.modules.admin.dto.PlanUserAllDTO;
import org.springblade.modules.admin.dto.RobotContentDTO;
import org.springblade.modules.admin.dto.rescue.PlanAddUserDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springblade.core.mp.base.BaseEntity;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.admin.dto.rescue.PlanAddUserSearchDTO;
import org.springblade.modules.admin.dto.rescue.RescueListDTO;
import org.springblade.modules.admin.dto.sugar.rescue.SugarRescueSearchDTO;
import org.springblade.modules.admin.entity.*;
import org.springblade.modules.admin.enums.*;
import org.springblade.modules.admin.event.TlwRescueInfoEvent;
import org.springblade.modules.admin.service.*;
import org.springblade.modules.admin.mapper.MaintainMalfunctionRescueMapper;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.modules.admin.service.suger.IEstateTlwScreenService;
import org.springblade.modules.admin.utils.AsyncUtils;
import org.springblade.modules.admin.utils.MapUtil;
import org.springblade.modules.admin.vo.MaintainPlanContentVO;
import org.springblade.modules.admin.vo.MapUserInfoDetailVO;
import org.springblade.modules.admin.vo.MapUserInfoVO;
import org.springblade.modules.admin.vo.*;
import org.springblade.modules.admin.vo.plan.PlanRescueDetailVO;
import org.springblade.modules.admin.vo.plan.TodayTaskListVO;
import org.springblade.modules.admin.vo.plan.wx.UserTodayPlanDetailVo;
import org.springblade.modules.admin.vo.rescue.*;
import org.springblade.modules.admin.vo.rescue.map.*;
import org.springblade.modules.admin.vo.sugar.rescue.*;
import org.springblade.modules.admin.vo.sugar.rescue.RescueAreaVO;
import org.springblade.modules.admin.vo.sugar.rescue.RescueOverviewVO;
import org.springblade.modules.admin.vo.sugar.StatisticsTrendVO;
import org.springblade.modules.notice.core.event.NoticeEvent;
import org.springblade.modules.notice.dto.NoticeTaskParam;
import org.springblade.modules.notice.enums.BusinessType;
import org.springblade.modules.notice.enums.TriggerConditionEnum;
import org.springblade.modules.notice.enums.TriggerNodeEnum;
import org.springblade.modules.system.entity.Dict;
import org.springblade.modules.system.entity.TenantConfig;
import org.springblade.modules.system.entity.User;
import org.springblade.modules.system.service.IDeptService;
import org.springblade.modules.system.service.IUserService;
import org.springblade.modules.system.service.TenantConfigService;
import org.springblade.modules.system.vo.SimpleUserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static org.springblade.modules.admin.service.BuildingService.PROJECT_TYPE_CODE;

/**
 * 救援工单明细 服务实现类
 *
 * @author ysq
 * @since 2023-02-25
 */
@Service
@Slf4j
public class MaintainMalfunctionRescueServiceImpl extends BaseServiceImpl<MaintainMalfunctionRescueMapper, MaintainMalfunctionRescue> implements IMaintainMalfunctionRescueService {

	@Lazy
	@Autowired
	private IMaintainPlanContentService maintainPlanContentService;

	@Autowired
	private IMaintainPlanContentUserService maintainPlanContentUserService;

	@Autowired
	private IMaintainMalfunctionHistoryService maintainMalfunctionHistoryService;

	@Autowired
	private MaintainMalfunctionLogService malfunctionLogService;

	@Autowired
	private IUserService userService;

	@Autowired
	private BuildingService buildingService;

	@Autowired
	private AsyncUtils asyncUtils;

	@Autowired
	private Environment environment;

	@Autowired
	private IMaintainPlanContentExtService maintainPlanContentExtService;

	@Autowired
	private IMaintainSignService maintainSignService;

	@Autowired
	private MqMessageService mqMessageService;

	@Autowired
	private IUserTrajectoryService userTrajectoryService;

	@Autowired
	private UserSignDetailService userSignDetailService;

	@Autowired
	private TenantConfigService tenantConfigService;

	@Autowired
	private ElevatorPrincipalDeptService elevatorPrincipalDeptService;

	@Autowired
	private IElevatorMaintainStatusDayService elevatorMaintainStatusDayService;

	@Autowired
	private IEstateTlwScreenService estateTlwScreenService;

	@Autowired
	private IRescueNoticeSessionService rescueNoticeSessionService;

	@Autowired
	private IBuildingExtService buildingExtService;
	@Autowired
	private ElevatorService elevatorService;

	@Autowired
	private ProjectApiConfiguration projectApiConfiguration;

	@Autowired
	private IMaintainSignCalendarService maintainSignCalendarService;

	@Autowired
	private ApplicationEventPublisher eventPublisher;

	@Autowired
	private BladeRedis bladeRedis;
	@Autowired
	private IDeptService deptService;

	@Autowired
	private MessageCenterUtils messageCenterUtils;

	/**
	 * redis催办key
	 */
	private static final String RESCUE_REMINDER_KEY = "rescue:reminder:key";

	@Override
	public IPage<RescueListVo> selectMaintainMalfunctionRescuePage(IPage<RescueListVo> page, RescueListDTO rescueListDTO) {
		List<RescueListVo> list = baseMapper.selectMaintainMalfunctionRescuePage(page, rescueListDTO);
		if (Func.isNotEmpty(list)) {
			List<Long> planIds = list.stream().map(RescueListVo::getId).collect(Collectors.toList());
			List<PlanUserAllDTO> planUserAllDTOS = maintainPlanContentService.selectPlanUserAll(planIds);
			Map<Long, List<PlanUserAllDTO>> planUserGroup = planUserAllDTOS.stream().collect(Collectors.groupingBy(PlanUserAllDTO::getPlanId));
			list.forEach(planListVo -> {
				List<PlanUserAllDTO> planUserAll = planUserGroup.get(planListVo.getId());
				if (Func.isNotEmpty(planUserAll)) {
					planListVo.setRealNames(planUserAll.stream().filter(e -> e.getPlanUserFromTable() != null && e.getPlanUserFromTable().equals(1)).map(PlanUserAllDTO::getRealName).collect(Collectors.joining(",")));
					planListVo.setPersonnelRealNames(planUserAll.stream().filter(e -> e.getPlanUserFromTable() != null && e.getPlanUserFromTable().equals(2)).map(PlanUserAllDTO::getRealName).collect(Collectors.joining(",")));
				}
			});
		}
		return page.setRecords(list);
	}

	@Override
	public PlanStatisticVo getPlanStatistic() {
		PlanStatisticVo planStatisticVo = new PlanStatisticVo();
		//  当前版本任务总数范围任务总数
		Integer planCounts = maintainPlanContentService.count(new LambdaQueryWrapper<MaintainPlanContent>()
			.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_KR.getId())
			.eq(BaseEntity::getIsDeleted, 0)
			.in(BaseEntity::getStatus,
				TaskStatusEnum.JY_DISPATCH.getStatus(),
				TaskStatusEnum.JY_PROCESSED.getStatus(),
				TaskStatusEnum.JY_ARRIVE.getStatus(),
				TaskStatusEnum.JY_RESCUE.getStatus(),
				TaskStatusEnum.JY_CANCEL.getStatus()
			)
		);
		planStatisticVo.setPlanCounts(planCounts);

		//根据工单状态查询 各状态任务
		Integer countNoHasUser = maintainPlanContentService.count(new LambdaQueryWrapper<MaintainPlanContent>()
			.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_KR.getId())
			.eq(BaseEntity::getStatus, TaskStatusEnum.JY_DISPATCH.getStatus())
		);
		planStatisticVo.setPlanNoHasUserCounts(countNoHasUser);

		Integer planHasSignCounts = maintainPlanContentService.count(new LambdaQueryWrapper<MaintainPlanContent>()
			.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_KR.getId())
			.eq(BaseEntity::getStatus, TaskStatusEnum.JY_PROCESSED.getStatus())
		);
		planStatisticVo.setPlanHasSignCounts(planHasSignCounts);

		Integer planFinishCounts = maintainPlanContentService.count(new LambdaQueryWrapper<MaintainPlanContent>()
			.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_KR.getId())
			.eq(BaseEntity::getStatus, TaskStatusEnum.JY_RESCUE.getStatus())
		);
		planStatisticVo.setPlanFinishCounts(planFinishCounts);

		// 已撤销
		Integer planCancelCounts = maintainPlanContentService.count(new LambdaQueryWrapper<MaintainPlanContent>()
			.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_KR.getId())
			.eq(BaseEntity::getStatus, TaskStatusEnum.JY_CANCEL.getStatus())
		);
		planStatisticVo.setPlanCancelCounts(planCancelCounts);

		// 已到达
		Integer planArriveCounts = maintainPlanContentService.count(new LambdaQueryWrapper<MaintainPlanContent>()
			.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_KR.getId())
			.eq(BaseEntity::getStatus, TaskStatusEnum.JY_ARRIVE.getStatus())
		);
		planStatisticVo.setPlanArriveCounts(planArriveCounts);

		// 新版平均用时
		Integer minutes = baseMapper.selectWorkMinutes();
		//计算平均用时
		if (Func.isNull(minutes) || 0 == planFinishCounts) {
			planStatisticVo.setWorkSecondDesc(null);
		} else {
			// 秒
			Integer workSeconds = minutes * 60;
			Integer avgSeconds = workSeconds / planFinishCounts;
			// 格式化
			String workSecondDesc = DateUtil.secondToTime(avgSeconds);
			planStatisticVo.setWorkSecondDesc(workSecondDesc);
		}

		return planStatisticVo;
	}

	@Override
	public RescuePlanDetail detail(Long planId) {
		RescuePlanDetail rescuePlanDetail = new RescuePlanDetail();
		//处理报警人信息
		MaintainMalfunctionRescue malfunctionRescue = this.getOne(Wrappers.<MaintainMalfunctionRescue>lambdaQuery().eq(MaintainMalfunctionRescue::getPlanId, planId));
		RescueUserInfoVo rescueUserInfoVo = new RescueUserInfoVo();
		rescueUserInfoVo.setUserName(malfunctionRescue.getName());
		rescueUserInfoVo.setPhone(malfunctionRescue.getPhone());
		rescueUserInfoVo.setRescueType(malfunctionRescue.getTrapType());
		rescueUserInfoVo.setRescueSourceName(malfunctionRescue.getTrapFromValue());
		rescueUserInfoVo.setCreateTime(malfunctionRescue.getCreateTime());
		rescueUserInfoVo.setRescueDesc(malfunctionRescue.getTrapDesc());
		rescuePlanDetail.setRescueUserInfoVo(rescueUserInfoVo);

		MaintainPlanContent maintainPlanContent = maintainPlanContentService.getById(planId);
		Long elevatorId = maintainPlanContent.getElevatorId();
		List<Long> elevatorIds = new ArrayList<>(16);
		elevatorIds.add(elevatorId);
		//处理电梯信息
		if (elevatorId != null && elevatorId != 0) {
			RescueElevatorInfoVo rescueElevatorInfo = getRescueElevatorInfo(elevatorId, planId, malfunctionRescue);
			rescuePlanDetail.setRescueElevatorInfo(rescueElevatorInfo);
		} else {
			RescueElevatorInfoVo rescueBuildInfo = baseMapper.getRescueBuildInfo(malfunctionRescue.getBuildingId());
			rescueBuildInfo.setElevatorAddress(malfunctionRescue.getAddress());
			rescueBuildInfo.setFloorNo(malfunctionRescue.getFloor());
			rescuePlanDetail.setRescueElevatorInfo(rescueBuildInfo);
		}
		List<UserInfoVo> userList4 = getUserList(elevatorIds, 4, planId, null);
		//救援人员
		rescuePlanDetail.setMaintainUsers(userList4);

		//救援记录说明
		rescuePlanDetail.setMaintainMalfunctionRescue(malfunctionRescue);

		List<MaintainMalfunctionHistory> maintainMalfunctionHistorys = maintainMalfunctionHistoryService.list(Wrappers.<MaintainMalfunctionHistory>lambdaQuery().eq(MaintainMalfunctionHistory::getPlanId, planId));
		for (MaintainMalfunctionHistory maintainMalfunctionHistory : maintainMalfunctionHistorys) {
			String realName = userService.getById(maintainMalfunctionHistory.getUpdateUser()).getRealName();
			maintainMalfunctionHistory.setUserName(realName);
		}
		rescuePlanDetail.setMaintainMalfunctionHistorys(maintainMalfunctionHistorys);

		//子维修工单
		List<PlanChildInfoVo> planChildInfos = baseMapper.getPlanChildInfo(planId);
		rescuePlanDetail.setPlanChildInfos(planChildInfos);

		rescuePlanDetail.setStatus(maintainPlanContent.getStatus());

		//工单进度时间，调度中、已出发、已完成—>工单更新时间； 已到达->拓展表到达时间
		MaintainPlanContent thisPlan = maintainPlanContentService.getOne(
			new LambdaQueryWrapper<MaintainPlanContent>()
				.eq(BaseEntity::getId, planId));
		if (Func.isNull(thisPlan)) {
			throw new ServiceException("业务异常，该工单不存在！");
		}
		//已到达时时间为拓展表到达时间
		if (TaskStatusEnum.JY_ARRIVE.getStatus() == thisPlan.getStatus()) {
			// 获取拓展信息
			MaintainPlanContentExt planContentExt = maintainPlanContentExtService.getOne(
				new LambdaQueryWrapper<MaintainPlanContentExt>()
					.eq(MaintainPlanContentExt::getPlanId, planId));
			if (Func.isNull(planContentExt)) {
				throw new ServiceException("业务异常，工单信息不完整，请完善救援工单信息！");
			}
			rescuePlanDetail.setPlanTime(planContentExt.getArriveTime());
		} else {
			// 获取更新时间
			rescuePlanDetail.setPlanTime(thisPlan.getUpdateTime());
		}

		return rescuePlanDetail;
	}


	/**
	 * @param elevatorIds 电梯id
	 * @param type        1:电梯责任人2:负责组，3：上次维保人员,4:分配人员
	 * @param planId
	 * @param userName
	 * @return
	 */
	@Override
	public List<UserInfoVo> getUserList(List<Long> elevatorIds, Integer type, Long planId, String userName) {

		Elevator elevator = elevatorService.getById(elevatorIds.get(0));


		List<UserInfoVo> list = baseMapper.getUserList(elevatorIds, type, planId, userName);
		List<Long> userIds = list.stream().map(UserInfoVo::getUserId).distinct().collect(Collectors.toList());
		//处理待处理的工单
		if ((type == 1 || type == 3) && userIds.size() > 0 && elevator != null) {
			Building building = buildingService.getById(elevator.getBuildingId());
			List<MaintainPlanContent> planList = baseMapper.planListByUserIds(userIds);
			if (planList.size() > 0) {
				List<Long> planIds = planList.stream().map(MaintainPlanContent::getId).distinct().collect(Collectors.toList());
				Map<Long, List<MaintainPlanContent>> map1 = planList.stream().collect(Collectors.groupingBy(MaintainPlanContent::getUserId));
				List<MaintainPlanContent> signList = baseMapper.getsignListByPlanIds(null);
				Map<Long, List<MaintainPlanContent>> map2 = signList.stream().collect(Collectors.groupingBy(MaintainPlanContent::getUserId));
				for (UserInfoVo userInfoVo : list) {
					List<MaintainPlanContent> maintainPlanContentList1 = map1.get(userInfoVo.getUserId());
					List<MaintainPlanContent> maintainPlanContentList2 = map2.get(userInfoVo.getUserId());
					if (maintainPlanContentList1 != null && maintainPlanContentList1.size() > 0) {
						userInfoVo.setPlanCounts(maintainPlanContentList1.size());
						//单独处理救援
						List<MaintainPlanContent> planContents = maintainPlanContentList1.stream().filter(o -> o.getTypeStatus() == MaintainPlanContentEnum.MAINTAIN_KR.getId() && (o.getStatus() == TaskStatusEnum.JY_PROCESSED.getStatus() || o.getStatus() == TaskStatusEnum.JY_ARRIVE.getStatus())).collect(Collectors.toList());
						List<Integer> typeStatus = new ArrayList<>(1);
						if (planContents != null && planContents.size() > 0) {
							typeStatus.add(MaintainPlanContentEnum.MAINTAIN_KR.getId());
							userInfoVo.setTypeStatus(typeStatus);
						}
					}
					if (maintainPlanContentList2 != null && maintainPlanContentList2.size() > 0) {
						List<Integer> typeStatus = maintainPlanContentList2.stream().map(MaintainPlanContent::getTypeStatus).distinct().collect(Collectors.toList());
						List<Integer> typeStatus1 = userInfoVo.getTypeStatus();
						if (typeStatus1 != null && typeStatus1.size() > 0) {
							typeStatus.addAll(typeStatus1);
						}
						userInfoVo.setTypeStatus(typeStatus);
					}

				}
			}
			for (UserInfoVo userInfoVo : list) {
				if (userInfoVo.getTypeStatus() == null || userInfoVo.getTypeStatus().size() == 0) {
					List<Integer> typeStatus = new ArrayList<>(1);
					typeStatus.add(-1);
					userInfoVo.setTypeStatus(typeStatus);
				} else {
					userInfoVo.setTypeStatus(userInfoVo.getTypeStatus().stream().distinct().collect(Collectors.toList()));
				}
				if (StrUtil.isNotEmpty(userInfoVo.getLatitude()) && StrUtil.isNotEmpty(userInfoVo.getLongitude())) {
					Double distance = MapUtil.getDistanceToDouble(Convert.toDouble(userInfoVo.getLongitude()), Convert.toDouble(userInfoVo.getLatitude()), Convert.toDouble(building.getLongitude()), Convert.toDouble(building.getLatitude()));
					userInfoVo.setDistance(distance);
				}
			}
		}

		return list;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Boolean rescueCancel(Long planId, Integer type) {
		final BladeUser bladeUser = AuthUtil.getUser();
		MaintainPlanContent maintainPlanContent = maintainPlanContentService.getOne(Wrappers.<MaintainPlanContent>lambdaQuery().eq(MaintainPlanContent::getId, planId));
		if (maintainPlanContent == null) {
			throw new ServiceException("未找到对应工单");
		}
		if (maintainPlanContent.getStatus().equals(TaskStatusEnum.JY_CANCEL.getStatus())) {
			return true;
		}
		if (type == 1) {
			// 梯联网只有调度中、待开始、已出发状态下可以撤回工单
			List<Integer> checkCancelStatus = Arrays.asList(TaskStatusEnum.JY_DISPATCH.getStatus(), TaskStatusEnum.JY_TO_START.getStatus(), TaskStatusEnum.JY_PROCESSED.getStatus());
			if (checkCancelStatus.stream().noneMatch(t -> t.equals(maintainPlanContent.getStatus()))) {
				throw new ServiceException("救援人员已到达，不能撤销工单");
			}
		}
		maintainPlanContent.setStatus(TaskStatusEnum.JY_CANCEL.getStatus());
		maintainPlanContentService.updateById(maintainPlanContent);
		malfunctionLogService.insertWorkLog(planId, "撤销工单", maintainPlanContent.getStatus(), null, bladeUser.getUserId(), bladeUser.getNickName());
		// 发送通知
		eventPublisher.publishEvent(new NoticeEvent(NoticeTaskParam.builder()
			.businessType(BusinessType.RESCUE.getCode())
			.triggerNode(TriggerNodeEnum.CANCEL.getCode())
			.triggerCondition(TriggerConditionEnum.CONDITION_26.getCode())
			.bizId(maintainPlanContent.getId())
			.tenantId(maintainPlanContent.getTenantId())
			.build()
		));

		// 发送救援信息到MQ
		RescueOrderDetail rescueOrderDetail = this.rescueDetail(planId);
		if (Func.isNotEmpty(rescueOrderDetail)) {
			// 推送实时信息到梯联网
			eventPublisher.publishEvent(new TlwRescueInfoEvent(rescueOrderDetail));
		}

		return true;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Boolean planAddUser(PlanAddUserDTO planAddUserDTO) {
		String curEnvironment = environment.getActiveProfiles()[0];
		BladeUser bladeUser = AuthUtil.getUser();
		List<Long> userIds2 = new ArrayList<>(5);
		userIds2.addAll(planAddUserDTO.getUserIds());
		MaintainPlanContent maintainPlanContent = maintainPlanContentService.getOne(Wrappers.<MaintainPlanContent>lambdaQuery().eq(MaintainPlanContent::getId, planAddUserDTO.getPlanId()));
		if (maintainPlanContent == null) {
			throw new ServiceException("未找到对应的工单");
		}

		//判断状态是否一致，不一致表示小程序端操作工单
		if (!planAddUserDTO.getStatus().equals(maintainPlanContent.getStatus())) {
			throw new ServiceException("工单状态已经更新请重新选择");
		}
		final MaintainMalfunctionRescue malfunctionRescue = this.getOne(Wrappers.<MaintainMalfunctionRescue>lambdaQuery().eq(MaintainMalfunctionRescue::getPlanId, planAddUserDTO.getPlanId()));

		//如果调度中,删除旧人员，重新添加新人员
		List<MaintainPlanContentUser> contentUsers = maintainPlanContentUserService.list(Wrappers.<MaintainPlanContentUser>lambdaQuery().eq(MaintainPlanContentUser::getPlanId, planAddUserDTO.getPlanId()));
		List<Long> userIds = contentUsers.stream().map(MaintainPlanContentUser::getUserId).collect(Collectors.toList());
		if (userIds.size() > 0) {
			planAddUserDTO.getUserIds().removeAll(userIds);
			userIds2.addAll(userIds);
		}

		Date now = DateUtil.date();
		List<MaintainPlanContentUser> list = new ArrayList<>(16);
		for (Long userId : planAddUserDTO.getUserIds()) {
			MaintainPlanContentUser maintainPlanContentUser = new MaintainPlanContentUser();
			maintainPlanContentUser.setPlanId(planAddUserDTO.getPlanId());
			maintainPlanContentUser.setUserId(userId);
			maintainPlanContentUser.setCreateTime(now);
			list.add(maintainPlanContentUser);
		}
		//表示指派，其它表示转派
		Integer addType = 1;
		if (planAddUserDTO.getUserIds().size() == 0) {
			return true;
		}

		//sprint 44 将这个逻辑改为增派
		Long planId = planAddUserDTO.getPlanId();
		List<User> userList = userService.list(Wrappers.<User>lambdaQuery().in(User::getId, userIds2.stream().distinct().collect(Collectors.toList())));
		List<String> names = userList.stream().map(User::getRealName).collect(Collectors.toList());

		//当是增派模式时，根据该工单已有 userId 过滤
		if (!planAddUserDTO.getAddType().equals(addType)) {
			List<Long> planIds = new ArrayList<>();
			planIds.add(planId);
			List<MaintainPlanContentUser> maintainPlanContentUsers = maintainPlanContentUserService.listByPlanId(planIds);
			//获取当前已分配工单的用户id列表
			List<Long> userIdB = new ArrayList<>();
			maintainPlanContentUsers.forEach(m -> userIdB.add(m.getUserId()));
			//新提交的列表
			List<Long> userIdA = new ArrayList<>();
			userList.forEach(u -> userIdA.add(u.getId()));
			//获取差值
			names = new ArrayList<>();
			List<Long> difference = getDifference(userIdA, userIdB);
			for (int i = 0; i < difference.size(); i++) {
				Long diffUid = difference.get(i);
				User byId = userService.getById(diffUid);
				names.add(byId.getRealName());
			}
		}
		maintainPlanContentUserService.saveBatch(list);
		// 当 已到达时，状态不变
		if (TaskStatusEnum.JY_ARRIVE.getStatus() == maintainPlanContent.getStatus()) {
			maintainPlanContent.setStatus(TaskStatusEnum.JY_ARRIVE.getStatus());
		} else {
			maintainPlanContent.setStatus(TaskStatusEnum.JY_PROCESSED.getStatus());
		}
		maintainPlanContentService.updateById(maintainPlanContent);

		// 发送救援信息到MQ
		RescueOrderDetail rescueOrderDetail = this.rescueDetail(planId);
		if (Func.isNotEmpty(rescueOrderDetail)) {
			rescueOrderDetail.setMapUserInfoDetails(this.mapUserDetails(planId));
			// 推送实时信息到梯联网
			eventPublisher.publishEvent(new TlwRescueInfoEvent(rescueOrderDetail));
		}


		String desc = names.toString();
		if (planAddUserDTO.getAddType().equals(addType)) {
			desc = "指派人员" + desc;
		} else {
			desc = "增派人员" + desc;
		}
		malfunctionLogService.insertWorkLog(planAddUserDTO.getPlanId(), desc, maintainPlanContent.getStatus(), null, bladeUser.getUserId(), bladeUser.getNickName());

		//只发给未接收过消息的救援人员
		List<Long> userIds1 = planAddUserDTO.getUserIds();
		if (userIds1.size() > 0) {
			final List<UserInfoVo> userInfoVoList = baseMapper.getMessageUserInfoVos(userIds1);
			final Building building = buildingService.getById(malfunctionRescue.getBuildingId());
			for (UserInfoVo userInfoVo : userInfoVoList) {
				// 2025.7.17新增 未及时出发预警通知
				TenantConfig tenantConfig = tenantConfigService.getTenantConfig(maintainPlanContent.getTenantId());
				int overtimeNotDepart = (int) Double.parseDouble(String.valueOf(tenantConfig.getConfig().get("overtimeNotDepart")));
				long sendArriveTime = now.getTime() + Long.valueOf(overtimeNotDepart) * 60 * 1000;
				double notDepartMove = Double.parseDouble(String.valueOf(tenantConfig.getConfig().get("notDepartMove")));
				UserTrajectory userTrajectory = userTrajectoryService.getOne(Wrappers.<UserTrajectory>lambdaQuery().between(UserTrajectory::getCreateTime, DateUtil.beginOfDay(new Date()), new Date()).eq(UserTrajectory::getUserId, userInfoVo.getUserId()).orderByDesc(UserTrajectory::getCreateTime).last("limit 1"));
				JSONObject sendWarningMsg = new JSONObject();
				sendWarningMsg.put("planId", planAddUserDTO.getPlanId());
				sendWarningMsg.put("userId", userInfoVo.getUserId());
				sendWarningMsg.put("bType", RescueArriveEnum.OVERTIME_NOT_DEPART.getId());
				sendWarningMsg.put("longitude", userTrajectory == null ? null : userTrajectory.getLongitude());
				sendWarningMsg.put("latitude", userTrajectory == null ? null : userTrajectory.getLatitude());
				sendWarningMsg.put("notDepartMove", notDepartMove);
				sendWarningMsg.put("overtimeNotDepart", overtimeNotDepart);
				mqMessageService.sendRescueArrive(sendArriveTime, sendWarningMsg.toJSONString());

				//发送模板消息
				Map<String, String> templateDataMap = new HashMap<>(8);
				templateDataMap.put("first", "收到救援任务，请速去救援！");
				if (Func.isNotBlank(malfunctionRescue.getName()) && Func.isNotBlank(malfunctionRescue.getPhone())) {
					templateDataMap.put("keyword1", malfunctionRescue.getName() + "/" + malfunctionRescue.getPhone());
				} else {
					templateDataMap.put("keyword1", "一键救援");
				}
				templateDataMap.put("keyword2", building.getName() + "-" + malfunctionRescue.getAddress() + "-楼层:" + malfunctionRescue.getFloor());
				templateDataMap.put("keyword3", DateUtil.format(malfunctionRescue.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
				templateDataMap.put("keyword4", malfunctionRescue.getTrapDesc());
				templateDataMap.put("remark", "地址:" + building.getAddress());
				if (Func.equalsSafe(curEnvironment, "prod")) {
					String skipUrl = String.format("/subpages/rescue/rescueDetail?planId=%s&elevatorId=%s", maintainPlanContent.getId(), maintainPlanContent.getElevatorId());
					templateDataMap.put("params", skipUrl);
				}

				if (Func.isNotEmpty(userInfoVo.getOpenId())) {
					asyncUtils.sendWeiXinTemplate(userInfoVo.getOpenId(), getWeiXinTemplateId(), templateDataMap);
				}
				HashMap<String, String> smsMap = new HashMap<>(2);
				smsMap.put("status", "进行中");
				asyncUtils.sendSms(MaintenanceSmsConstant.RESCUE_ADD_USER, smsMap, userInfoVo.getPhone());
				Map<String, String> phoneMap = new HashMap<>(2);
				phoneMap.put("place", building.getName() + malfunctionRescue.getAddress() + "楼层:" + malfunctionRescue.getFloor());
//				VoiceTemplate voiceTemplate = new VoiceTemplate();
//				voiceTemplate.setTemplateCode(VoiceConstant.RESCUE_ADD_USER);
//				voiceTemplate.setTemplateParam(phoneMap);
//				voiceTemplate.setPhoneNumber(userInfoVo.getPhone());
//				asyncUtils.sendPhone(voiceTemplate);

				SendRequest request = SendRequest.builder()
					.messageTemplateId("1959864202326257665")
					.messageParam(
						MessageParam.builder()
							.receiver(userInfoVo.getPhone())
							.variables(phoneMap)
							.callback(cn.hutool.core.map.MapUtil.of("planId", maintainPlanContent.getId()))
							.build()
					).build();
				R r = messageCenterUtils.sendMessage(request);
				log.info("救援-分配人员-语音电话通知结果:" + JSON.toJSONString(r));
			}

			// 消息通知埋点
			String userNames = userInfoVoList.stream().map(UserInfoVo::getUserName).collect(Collectors.joining("/"));
			if (planAddUserDTO.getAddType().equals(addType)) {
				// 指派
				eventPublisher.publishEvent(new NoticeEvent(NoticeTaskParam.builder()
					.businessType(BusinessType.RESCUE.getCode())
					.triggerNode(TriggerNodeEnum.ASSIGN.getCode())
					.triggerCondition(TriggerConditionEnum.CONDITION_22.getCode())
					.bizId(maintainPlanContent.getId())
					.extra(cn.hutool.core.map.MapUtil.of("assignFixedText", String.format("【%s]】已调单【%s】前往", AuthUtil.getNickName(), userNames)))
					.tenantId(maintainPlanContent.getTenantId())
					.build()
				));
			} else {
				// 增派
				eventPublisher.publishEvent(new NoticeEvent(NoticeTaskParam.builder()
					.businessType(BusinessType.RESCUE.getCode())
					.triggerNode(TriggerNodeEnum.ASSIGN.getCode())
					.triggerCondition(TriggerConditionEnum.CONDITION_23.getCode())
					.bizId(maintainPlanContent.getId())
					.extra(cn.hutool.core.map.MapUtil.of("assignFixedText", String.format("【%s】已增派【%s】前往", AuthUtil.getNickName(), userNames)))
					.tenantId(maintainPlanContent.getTenantId())
					.build()
				));
			}
		}
		//2024-08-15 新增发送【救援已出发】机器人微信群小程序卡片
		if (planAddUserDTO.getAddType().equals(addType) && maintainPlanContent.getElevatorId() > 0) {
			RobotContentDTO robotContentDTO = new RobotContentDTO();
			robotContentDTO.setMaintainType(MaintainPlanContentEnum.MAINTAIN_KR.getId());
			robotContentDTO.setMaintainSubTypeDesc("【救援已出发】");
			ElevatorRescueRobotSimpleVO elevatorInfo = maintainPlanContentService.getElevatorSimpleInfoByPlanId(planId);
			robotContentDTO.setLocationDesc(elevatorInfo.getBuildingName() + "-" + elevatorInfo.getElevatorAddress());
			// 复用 malfunctionDesc 字段 存放 到达信息
			robotContentDTO.setMalfunctionDesc(elevatorInfo.getRescueTimeDesc());
			List<String> userName = new ArrayList<>();
			List<String> avatars = new ArrayList<>();
			List<Long> firstAddUsers = planAddUserDTO.getUserIds();
			firstAddUsers.forEach(f -> {
				SimpleUserVO simpleUserInfo = userService.getSimpleUserInfo(f);
				userName.add(simpleUserInfo.getUserName());
				avatars.add(simpleUserInfo.getAvatar());
			});
			robotContentDTO.setMaintainPersonDesc(String.join(",", userName));
			robotContentDTO.setMaintainPersonImgDesc(String.join(",", avatars));
			asyncUtils.createPost(
				projectApiConfiguration.getMaintain() + "/repair/robot/sendRobotMessage/" + maintainPlanContent.getId() + "/" + maintainPlanContent.getElevatorId(),
				JSONObject.toJSONString(robotContentDTO),
				WebUtil.getRequest()
			);
		}
		// 2025-07 planContentExt非空插入allocate_time
		List<MaintainPlanContentExt> planExtList = maintainPlanContentExtService.list(new LambdaQueryWrapper<MaintainPlanContentExt>()
			.eq(MaintainPlanContentExt::getPlanId, planId)
			.eq(BaseEntity::getIsDeleted, 0)
		);
		if(Func.isNotEmpty(planExtList)){
			MaintainPlanContentExt maintainPlanContentExt = planExtList.get(0);
			if(Func.isEmpty(maintainPlanContentExt.getAllocateTime())){
				Date allocateTime = DateUtil.date();
				maintainPlanContentExt.setAllocateTime(allocateTime);
				maintainPlanContentExt.setAllocateDuration(allocateTime.getTime() - maintainPlanContent.getCreateTime().getTime());
				maintainPlanContentExtService.updateById(maintainPlanContentExt);
			}
		}
		return true;
	}

	@Override
	public RescueElevatorInfoVo getRescueElevatorInfo(Long elevatorId, Long planId, MaintainMalfunctionRescue malfunctionRescue) {
		List<Long> elevatorIds = new ArrayList<>(1);
		elevatorIds.add(elevatorId);
		if (malfunctionRescue == null && planId != null) {
			malfunctionRescue = this.getOne(Wrappers.<MaintainMalfunctionRescue>lambdaQuery().eq(MaintainMalfunctionRescue::getPlanId, planId));
		}
		RescueElevatorInfoVo rescueElevatorInfo = baseMapper.getRescueElevatorInfo(elevatorId);
		if (malfunctionRescue != null) {
			rescueElevatorInfo.setElevatorAddress(malfunctionRescue.getAddress());
			rescueElevatorInfo.setFloorNo(malfunctionRescue.getFloor());
		}
		List<UserInfoVo> userList3 = getUserList(elevatorIds, 3, planId, null);
		List<UserInfoVo> userList2 = getUserList(elevatorIds, 2, planId, null);
		List<UserInfoVo> userList1 = getUserList(elevatorIds, 1, planId, null);

		//上次维保人员
		rescueElevatorInfo.setLastPlanUsers(userList3);

		//责任人
		rescueElevatorInfo.setElevatorResponsible(userList1);

		//责任组
		rescueElevatorInfo.setGroupInfos(userList2);

		return rescueElevatorInfo;
	}


	/**
	 * 获取模板消息id
	 *
	 * @return
	 */
	private String getWeiXinTemplateId() {
		String curEnvironment = environment.getActiveProfiles()[0];
		if (Func.equalsSafe(curEnvironment, "prod")) {
			return TemplateMsgConstant.RESCUE_MESSAGE_PROD;
		} else {
			return TemplateMsgConstant.RESCUE_MESSAGE_TEST;
		}
	}

	/**
	 * 分页查询已出发工单，且仅一人执行，或其它执行人均已离职
	 * 复用RescueListDTO，selectMaintainMalfunctionRescuePage方法
	 *
	 * @param rescueListDTO 查询条件
	 * @return 分页结果
	 */
	@Override
	public IPage<RescueListVo> selectMaintainRescueOnlyHim(RescueListDTO rescueListDTO) {
		Query query = new Query();
		query.setSize(rescueListDTO.getSize());
		query.setCurrent(rescueListDTO.getCurrent());
		IPage<RescueListVo> page = Condition.getPage(query);
		List<RescueListVo> list = baseMapper.selectMaintainMalfunctionRescuePage(rescueListDTO.getSize() == 0 ? null : page, rescueListDTO);

		//只有当前用户为进行中的结果
		List<RescueListVo> collect
			= list.stream().filter(p -> p.getMaintainName().split(",").length == 1).collect(Collectors.toList());

		//多人执行，但其它人均已离职的情况
		//获取多人执行的工单
		List<RescueListVo> temp
			= list.stream().filter(p -> p.getMaintainName().split(",").length > 1).collect(Collectors.toList());

		//遍历该工单列表
		if (Func.isNotEmpty(temp)) {
			temp.forEach(t -> {
				boolean flag = true;
				List<User> maintainUsers = new ArrayList<>();
				//获取救援人员信息
				String[] maintainNames = t.getMaintainName().split(",");
				if (Func.isNotEmpty(maintainNames)) {
					for (int i = 0; i < maintainNames.length; i++) {
						//根据括号拆分,获取第一个字符串，即为第user real_name
						String[] split = maintainNames[i].replaceAll("\"", "").split("[\\(\\)]");
						//获取救援人
						User maintainUser = userService.getOne(new LambdaQueryWrapper<User>()
							.eq(User::getRealName, split[0])
							.eq(User::getPhone, split[1])
							.eq(TenantEntity::getTenantId, AuthUtil.getTenantId())
						);
						if (!ObjectUtils.isNull(maintainUser)) {
							//获取将救援人列表
							maintainUsers.add(maintainUser);
						}
					}
				}
				// 遍历救援人员列表，减去自己，剩下的查看是否已离职，只要存在没离职的，flag = false，不需要插入该工单
				int count = 0;
				for (User user : maintainUsers) {
					if (2 != user.getStatus()) {
						count++;
					}
				}
				if (count > 1) {
					flag = false;
				}
				if (flag) {
					collect.add(t);
				}
			});
		}
		// 二次分页
		List<RescueListVo> rescueListVos = startPage(collect, rescueListDTO.getCurrent(), rescueListDTO.getSize());
		page.setRecords(rescueListVos);
		page.setTotal(collect.size());
		return page;
	}

	// 二次分页
	public List startPage(List list, Integer pageNum, Integer pageSize) {
		if (list == null) {
			return Collections.emptyList();
		}
		if (list.isEmpty()) {
			return Collections.emptyList();
		}
		//记录总数
		Integer count = list.size();
		//开始索引
		int fromIndex = (pageNum - 1) * pageSize;
		//结束索引
		int toIndex = pageNum * pageSize;
		if (fromIndex + 1 > count) {
			return Collections.emptyList();
		}
		if (pageNum * pageSize > count) {
			toIndex = count;
		}
		return list.subList(fromIndex, toIndex);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean rescueConfirm(Long userId, Long planId) {
		// 1. 参数及参数对应的实体校验
		if (Func.isNull(userId) || Func.isNull(planId)) {
			throw new ServiceException("确认失败，参数不完整！");
		}
		// 获取用户信息
		User confirmUser = userService.getById(userId);
		if (Func.isNull(confirmUser)) {
			throw new ServiceException("确认失败，救援人员不存在！");
		}
		// 获取工单信息
		MaintainPlanContent confirmPlan = maintainPlanContentService.getOne(
			new LambdaQueryWrapper<MaintainPlanContent>()
				.eq(BaseEntity::getId, planId)
		);
		if (Func.isNull(confirmPlan)) {
			throw new ServiceException("确认失败，该救援工单不存在,请先创建救援工单");
		}
		// 当且仅当 工单状态为3、4 的时候，才能变成已到达（防止已撤销的情况）
		Integer status = confirmPlan.getStatus();
		boolean flag = status.equals(TaskStatusEnum.JY_PROCESSED.getStatus()) || status.equals(TaskStatusEnum.JY_ARRIVE.getStatus());
		if (!flag) {
			throw new ServiceException("已到达失败，只有状态为已出发才能已到达！");
		}
		boolean isPush = !status.equals(TaskStatusEnum.JY_ARRIVE.getStatus());
		// 获取工单明细对应的电梯、楼盘信息，经纬度
		List<MaintainMalfunctionRescue> rescuesList = this.list(
			Wrappers.<MaintainMalfunctionRescue>lambdaQuery()
				.eq(MaintainMalfunctionRescue::getPlanId, planId)
				.eq(BaseEntity::getIsDeleted, 0)
		);
		if (Func.isEmpty(rescuesList) || rescuesList.size() != 1) {
			throw new ServiceException("确认失败！请确认救援信息是否正确");
		}
		MaintainMalfunctionRescue confirmRescue = rescuesList.get(0);
		if (Func.isNull(confirmRescue)) {
			throw new ServiceException("确认失败！请确认救援信息是否正确");
		}
		// 获取项目经纬度、地址
		Long buildingId = confirmRescue.getBuildingId();
		if (Func.isNull(buildingId)) {
			throw new ServiceException("确认失败！救援工单未指定项目楼盘！");
		}
		Building confirmBuilding = buildingService.getById(buildingId);
		if (Func.isNull(confirmBuilding)) {
			throw new ServiceException("确认失败！救援工单未指定项目楼盘！");
		}
		// 经度、纬度、地址
		BigDecimal confirmLongitude = confirmBuilding.getLongitude();
		BigDecimal confirmLatitude = confirmBuilding.getLatitude();
		String confirmAddress = confirmBuilding.getAddress();
		// 2. 当前用户签到,插入签到所需全部信息
		// 救援模块 校验是否已 签到，是的情况抛异常
		List<MaintainSign> signList = maintainSignService.list(new LambdaQueryWrapper<MaintainSign>()
			.eq(MaintainSign::getPlanId, planId)
			.eq(MaintainSign::getUserId, userId)
		);
		if (signList.size() > 0) {
			throw new ServiceException("点击已到达时后台会默认签到，救援工单不能重复签到！");
		}

		MaintainSign arriveSign = signProperties(planId, userId, confirmLongitude, confirmLatitude, confirmAddress);
		// 签到
		boolean sign = maintainSignService.save(arriveSign);
		maintainPlanContentUserService.updatePlanUserSignInfo(planId, SignEnum.SIGN.getId(), userId, SignErrorTypeEnum.NORMAL.getId());
		if (!sign) {
			throw new ServiceException("确认失败，该人员代签失败，请联系系统管理员");
		}
		// 3. 当前工单状态改为 4=已到达
		boolean changeStatus = false;
		// 若当前工单状态已经为已到达，不变
		if (TaskStatusEnum.JY_ARRIVE.getStatus() == confirmPlan.getStatus()) {
			changeStatus = true;
		} else {
			// 不为已到达，工单更新为已到达
			confirmPlan.setStatus(TaskStatusEnum.JY_ARRIVE.getStatus());
			changeStatus = maintainPlanContentService.updateById(confirmPlan);
			if (!changeStatus) {
				throw new ServiceException("确认失败，系统异常，无法更改救援状态为已到达，请联系系统管理员");
			}
		}
		// 4. 工单log记录表插入救援状态转换记录
		User arrival = userService.getById(userId);
		JSONObject errorLogExt = new JSONObject();
		errorLogExt.put("signStatus", SignErrorTypeEnum.NORMAL.getId());
		errorLogExt.put("locationAddress", confirmAddress);
		malfunctionLogService.insertWorkLog(planId, arrival.getRealName() + TaskStatusEnum.JY_ARRIVE.getName(), TaskStatusEnum.JY_ARRIVE.getStatus(), errorLogExt.toJSONString());

		// 5. 拓展表插入已到达时间
		Date arriveTime = new Date();
		MaintainPlanContentExt confirmExt = maintainPlanContentExtService.getOne(
			new LambdaQueryWrapper<MaintainPlanContentExt>()
				.eq(MaintainPlanContentExt::getPlanId, planId)
		);
		if (Func.isNull(confirmExt)) {
			confirmExt = new MaintainPlanContentExt();
			confirmExt.setPlanId(planId);
		}
		if (Func.isNull(confirmExt.getArriveTime())) {
			confirmExt.setArriveTime(arriveTime);
			confirmExt.setArriveDuration(arriveTime.getTime() - confirmPlan.getCreateTime().getTime());
			maintainPlanContentExtService.saveOrUpdate(confirmExt);
		}

		// 6. 若超时到达，插入工单log
		if (DateUtil.compare(arriveTime, confirmRescue.getMaximumArrivalTime(), DatePattern.NORM_DATETIME_MINUTE_PATTERN) > 0) {
			// 多人作业，工单超时到达标签只打一次
			if (confirmRescue.getWarningList() == null || !Func.toIntList(confirmRescue.getWarningList()).contains(RescueArriveEnum.OVERTIME_ARRIVE.getId())) {
				confirmRescue.setArrivalWarning(RescueArriveEnum.OVERTIME_ARRIVE.getId());
				confirmRescue.setArrivalWarningTime(arriveTime);
				List<Integer> oldWarningList = new ArrayList<>();
				if (Func.isNotBlank(confirmRescue.getWarningList())) {
					oldWarningList = Arrays.stream(confirmRescue.getWarningList().split(","))
						.filter(s -> Func.isNotBlank(s)) // 过滤空字符串
						.map(String::trim) // 去除前后空格
						.map(Integer::parseInt) // 转换为 Integer
						.collect(Collectors.toList());
				}
				oldWarningList.add(RescueArriveEnum.OVERTIME_ARRIVE.getId());
				confirmRescue.setWarningList(oldWarningList.stream().distinct().map(e -> String.valueOf(e)).collect(Collectors.joining( ",")));
				this.updateById(confirmRescue);

				JSONObject logExt = new JSONObject();
				logExt.put("bType", RescueArriveEnum.OVERTIME_ARRIVE.getId());
				logExt.put("timeOut", DateUtil.formatDateTime(confirmRescue.getMaximumArrivalTime()));
				malfunctionLogService.insertWorkLog(planId, WorkTrackStatusEnum.OVERTIME_ARRIVE.getTypeName(),  TaskStatusEnum.JY_ARRIVE.getStatus(), logExt.toJSONString());

				// 消息通知
				eventPublisher.publishEvent(new NoticeEvent(NoticeTaskParam.builder()
					.businessType(BusinessType.RESCUE.getCode())
					.triggerNode(TriggerNodeEnum.SIGN_IN.getCode())
					.triggerCondition(TriggerConditionEnum.CONDITION_25.getCode())
					.bizId(planId)
					.tenantId(AuthUtil.getTenantId())
					.build()
				));
			}
		}

		// 发送救援信息到MQ
		RescueOrderDetail rescueOrderDetail = this.rescueDetail(planId);
		if (Func.isNotEmpty(rescueOrderDetail)) {
			rescueOrderDetail.setMapUserInfoDetails(this.mapUserDetails(planId));
			// 推送实时信息到梯联网
			eventPublisher.publishEvent(new TlwRescueInfoEvent(rescueOrderDetail));
		}
		// 2024-08-19 新增发送【救援已到达】机器人微信群小程序卡片
		// 电梯已知的情况下发送,且已到达只发送一次
		if (confirmPlan.getElevatorId() > 0 && isPush) {
			RobotContentDTO robotContentDTO = new RobotContentDTO();
			robotContentDTO.setMaintainType(MaintainPlanContentEnum.MAINTAIN_KR.getId());
			robotContentDTO.setMaintainSubTypeDesc("【救援已到达】");
			ElevatorRescueRobotSimpleVO elevatorInfo = maintainPlanContentService.getElevatorSimpleInfoByPlanId(planId);
			robotContentDTO.setLocationDesc(elevatorInfo.getBuildingName() + "-" + elevatorInfo.getElevatorAddress());
			// 复用 malfunctionDesc 字段 存放 到达信息
			robotContentDTO.setMalfunctionDesc(elevatorInfo.getRescueTimeDesc());
			List<String> userName = new ArrayList<>();
			List<String> avatars = new ArrayList<>();
			// 获取当前项目计划执行人
			List<Long> firstAddUsers = maintainPlanContentUserService.list(new LambdaQueryWrapper<MaintainPlanContentUser>()
				.eq(MaintainPlanContentUser::getPlanId, planId)
				.eq(BaseEntity::getIsDeleted, 0)
			).stream().map(MaintainPlanContentUser::getUserId).collect(Collectors.toList());

			firstAddUsers.forEach(f -> {
				SimpleUserVO simpleUserInfo = userService.getSimpleUserInfo(f);
				userName.add(simpleUserInfo.getUserName());
				avatars.add(simpleUserInfo.getAvatar());
			});
			robotContentDTO.setMaintainPersonDesc(String.join(",", userName));
			robotContentDTO.setMaintainPersonImgDesc(String.join(",", avatars));
			asyncUtils.createPost(
				projectApiConfiguration.getMaintain() + "/repair/robot/sendRobotMessage/" + planId + "/" + confirmPlan.getElevatorId(),
				JSONObject.toJSONString(robotContentDTO),
				WebUtil.getRequest()
			);
		}
		return true;
	}

	/**
	 * 方法大于80行，赋值行为抽取
	 *
	 * @return 赋值后的签到对象
	 */
	private MaintainSign signProperties(Long planId,
										Long userId,
										BigDecimal confirmLongitude,
										BigDecimal confirmLatitude,
										String confirmAddress) {
		MaintainSign arriveSign = new MaintainSign();
		Date signTime = new Date();
		arriveSign.setPlanId(planId);
		arriveSign.setUserId(userId);
		// 签到信息：经度、纬度、地址、状态、系统代签、签到时间
		arriveSign.setSignLongitude(confirmLongitude.toString());
		arriveSign.setSignLatitude(confirmLatitude.toString());
		arriveSign.setSignAddress(confirmAddress);
		arriveSign.setSignStatus(SignEnum.SIGN.getId());
		arriveSign.setSignTime(signTime);
		arriveSign.setStatus(2);
		// 签退信息：签退经度、纬度、地址、状态、间隔、签退时间
		arriveSign.setSignOutLongitude(confirmLongitude.toString());
		arriveSign.setSignOutLatitude(confirmLatitude.toString());
		arriveSign.setSignAddress(confirmAddress);
		arriveSign.setTimeInterval(0L);
		arriveSign.setSignOutStatus(SignEnum.SIGN.getId());
		arriveSign.setSignOutTime(signTime);
		return arriveSign;
	}


	/**
	 * 获取差集
	 *
	 * @param listA 集合A
	 * @param listB 集合A子集B
	 * @param <T>   占位符
	 * @return 差集
	 */
	public <T> List<T> getDifference(List<T> listA, List<T> listB) {
		Set<T> setB = new HashSet<>(listB);
		List<T> diffList = new ArrayList<>();

		for (T element : listA) {
			if (!setB.contains(element)) {
				diffList.add(element);
			}
		}

		return diffList;
	}


	@Override
	public List<MapUserInfoVO> mapUserList(List<Integer> userStatus, Long planId) {
		List<MapUserInfoVO> userInfoVOList = baseMapper.mapUserList(null, null);
		if (Func.isNotEmpty(userInfoVOList)) {
			if (Func.isNotEmpty(userStatus)) {
				userInfoVOList = userInfoVOList.stream().filter(t -> {
					for (Integer status : userStatus) {
						switch (status) {
							case 1:
							case 2:
							case 3:
								if (t.getPlanTypes().contains(status)) {
									return true;
								}
								break;
							case 4:
								if (t.getPlanTypes().size() == 0) {
									return true;
								}
								break;
							case 5:
								if (t.getIsSign()) {
									return true;
								}
						}
					}
					return false;
				}).collect(Collectors.toList());
			}
		}

		if (Func.isNotEmpty(planId)) {
			// 救援人员到达情况
			List<MaintainSign> maintainSigns = maintainSignService.list(Wrappers.<MaintainSign>lambdaQuery().eq(MaintainSign::getPlanId, planId));
			Map<Long, MaintainSign> signMap = maintainSigns.stream().collect(Collectors.groupingBy(t -> t.getUserId(), Collectors.collectingAndThen(Collectors.toList(), t -> t.get(0))));
			for (MapUserInfoVO mapUserInfo : userInfoVOList) {
				if (signMap.containsKey(mapUserInfo.getUserId())) {
					mapUserInfo.setIsArrived(Boolean.TRUE);
				} else {
					mapUserInfo.setIsArrived(Boolean.FALSE);
				}
			}
		}

		return userInfoVOList;
	}

	@Override
	public List<MapUserInfoVO> screenMapUserList(String search, String vehicles, String isSign, String planTypes, Integer searchMin, boolean isOnlyShowRescue) {
		TenantConfig tenantConfig = tenantConfigService.getTenantConfig(AuthUtil.getTenantId());
		String screenRoles = tenantConfig.getScreenRoles();
		List<MapUserInfoVO> userInfoVOList = baseMapper.mapUserList(screenRoles, null);
		if (Func.isNotEmpty(userInfoVOList)) {
			if (Func.isNotBlank(search)) {
				userInfoVOList = userInfoVOList.stream().filter(t -> {
					if (t.getRealName() != null && t.getRealName().contains(search)) {
						return Boolean.TRUE;
					}
					if (t.getUserZhCode().contains(search)) {
						return Boolean.TRUE;
					}
					return Boolean.FALSE;
				}).collect(Collectors.toList());
			}
			if (isOnlyShowRescue) {
				userInfoVOList = userInfoVOList.stream().filter(t -> t.getPlanTypes().contains(MaintainPlanContentEnum.MAINTAIN_KR.getId())).collect(Collectors.toList());
			}
			userInfoVOList = this.mapUserDataFilter(userInfoVOList, Func.toStrList(vehicles), Func.toIntList(planTypes), Func.toIntList(isSign), searchMin,  null,null);
//			if(Func.isNotBlank(vehicles)){
//				userInfoVOList = userInfoVOList.stream().filter(t -> Func.isNotBlank(t.getVehicle()) && vehicles.contains(t.getVehicle())).collect(Collectors.toList());
//			}
//			if(Func.isNotBlank(isSign)){
//				if(isSign.length() == 1 && isSign.contains("1")){
//					userInfoVOList = userInfoVOList.stream().filter(t -> Func.isNotEmpty(t.getIsSign()) && t.getIsSign()).collect(Collectors.toList());
//				}
//				if(isSign.length() == 1 && isSign.contains("2")){
//					userInfoVOList = userInfoVOList.stream().filter(t -> Func.isNotEmpty(t.getIsSign()) && !t.getIsSign()).collect(Collectors.toList());
//				}
//			}
//			if(Func.isNotBlank(planTypes)){
//				String searchPlanTypes = Func.toStrList(planTypes).stream().sorted().collect(Collectors.joining());
//				userInfoVOList = userInfoVOList.stream().filter(t -> {
//					if(Func.isNotEmpty(t.getPlanTypes())){
//						String userPlanTypes = t.getPlanTypes().stream().map(String::valueOf).sorted().collect(Collectors.joining());
//						if(searchPlanTypes.contains(userPlanTypes) || userPlanTypes.contains(searchPlanTypes)){
//							return Boolean.TRUE;
//						}
//					}
//					return Boolean.FALSE;
//				}).collect(Collectors.toList());
//			}
//			if(Func.isNotEmpty(searchMin) && searchMin > 0){
//				userInfoVOList = userInfoVOList.stream().filter(t -> DateUtil.date().getTime() - t.getCreateTime().getTime() <= searchMin*60*1000+59*1000+999).collect(Collectors.toList());
//			}
		}
		return userInfoVOList;
	}

	@Override
	public List<MapUserInfoCardDetailVO> mapUserCardDetails(List<Long> userIds, Long elevatorId, Long planId) {
		// 救援人员基本信息
		List<MapUserInfoCardDetailVO> baseUserInfo = baseMapper.mapBaseUserInfos(userIds, elevatorId);
		// 救援人员到达情况
		List<MaintainSign> maintainSigns = maintainSignService.list(Wrappers.<MaintainSign>lambdaQuery().in(MaintainSign::getUserId, userIds).eq(MaintainSign::getPlanId, planId));
		Map<Long, MaintainSign> signMap = maintainSigns.stream().collect(Collectors.groupingBy(t -> t.getUserId(), Collectors.collectingAndThen(Collectors.toList(), t -> t.get(0))));

		// 救援人员今日任务统计
		List<MaintainPlanContentVO> curDatePlans = baseMapper.curDatePlan(userIds);
		Map<Long, Map<Integer, Long>> planStatisticsMap = curDatePlans.stream().collect(Collectors.groupingBy(t -> t.getUserId(), Collectors.groupingBy(t -> t.getTypeStatus(), Collectors.counting())));

		for (MapUserInfoCardDetailVO userInfo : baseUserInfo) {
			if (signMap.containsKey(userInfo.getUserId())) {
				// 用户已到达
				MaintainSign maintainSign = signMap.get(userInfo.getUserId());
				userInfo.setIsArrived(Boolean.TRUE);
				userInfo.setArriveTime(maintainSign.getSignTime());
				userInfo.setLongitude(maintainSign.getSignLongitude());
				userInfo.setLatitude(maintainSign.getSignLatitude());
			} else {
				userInfo.setIsArrived(Boolean.FALSE);
				// 获取人员今日统计
				if (planStatisticsMap.containsKey(userInfo.getUserId())) {
					userInfo.setPlanStatistics(planStatisticsMap.get(userInfo.getUserId()));
				}
			}
		}

		return baseUserInfo;
	}

	@Override
	public MapUserInfoCardDetailVO mapUserScreenDetail(Long userId) {
		// 救援人员基本信息
		List<MapUserInfoCardDetailVO> baseUserInfos = baseMapper.mapBaseUserInfos(Arrays.asList(userId), null);
		if (Func.isEmpty(baseUserInfos)) {
			return null;
		}

		MapUserInfoCardDetailVO baseUserInfo = baseUserInfos.get(0);
		// 实时定位
		TenantConfig tenantConfig = tenantConfigService.getTenantConfig(AuthUtil.getTenantId());
		String screenRoles = tenantConfig.getScreenRoles();
		List<MapUserInfoVO> userInfoVOList = baseMapper.mapUserList(screenRoles, null);
		Map<Long, MapUserInfoVO> trajectoryMap = userInfoVOList.stream().collect(Collectors.toMap(t -> t.getUserId(), t -> t));
		if (trajectoryMap.containsKey(baseUserInfo.getUserId())) {
			baseUserInfo.setLatitude(trajectoryMap.get(baseUserInfo.getUserId()).getLatitude());
			baseUserInfo.setLongitude(trajectoryMap.get(baseUserInfo.getUserId()).getLongitude());
			baseUserInfo.setPositionDate(trajectoryMap.get(baseUserInfo.getUserId()).getCreateTime());
		}
		// 出勤情况
		UserSignDetail userSignDetail = userSignDetailService.getCurDateSign(baseUserInfo.getUserId());
		baseUserInfo.setSignStatus(userSignDetail == null ? 0 : userSignDetail.getSignStatus());

		// 个人工单情况统计
		Map<Long, Map<Integer, Long>> planStatistics = this.getPlanStatistics(Arrays.asList(baseUserInfo.getUserId()));
		baseUserInfo.setPlanStatistics(planStatistics.get(baseUserInfo.getUserId()));

		return baseUserInfo;
	}

	/**
	 * 个人工单情况统计数据封装
	 *
	 * @param userIds
	 * @return
	 */
	private Map<Long, Map<Integer, Long>> getPlanStatistics(List<Long> userIds) {
		Map<Long, Map<Integer, Long>> allUserPlanStatistics = new HashMap<>(16);
		if (Func.isEmpty(userIds)) {
			return allUserPlanStatistics;
		}
		// 个人工单情况统计
		List<MaintainPlanContent> planContentList = maintainPlanContentService.screenUserPlanStatisticsList(userIds);
		Map<Long, List<MaintainPlanContent>> planContentMap = planContentList.stream().collect(Collectors.groupingBy(t -> t.getUserId()));
		planContentMap.forEach((k, v) -> {
			Map<Integer, Long> planStatistics = new HashMap<>(4);
			// 已完成保养工单数
			planStatistics.put(0, v.stream().filter(t -> t.getTypeStatus() == 1 && t.getStatus() >= 3).count());
			// 总保养数
			planStatistics.put(1, v.stream().filter(t -> t.getTypeStatus() == 1).count());
			// 尚未完成维修数
			planStatistics.put(2, v.stream().filter(t -> t.getTypeStatus() == 2).count());
			// 尚未完成救援数
			planStatistics.put(3, v.stream().filter(t -> t.getTypeStatus() == 3).count());
			allUserPlanStatistics.put(k, planStatistics);
		});
		return allUserPlanStatistics;
	}

	@Override
	public List<MapUserInfoDetailVO> mapUserDetails(Long planId) {
		List<MapUserInfoDetailVO> details = baseMapper.mapUserDetails(planId);
		// 救援人员到达情况
		List<Long> userIds = details.stream().map(t -> t.getUserId()).collect(Collectors.toList());
		Map<Long, MaintainSign> signMap = new HashMap<>();
		if (Func.isNotEmpty(userIds)) {
			List<MaintainSign> maintainSigns = maintainSignService.list(Wrappers.<MaintainSign>lambdaQuery().in(MaintainSign::getUserId, userIds).eq(MaintainSign::getPlanId, planId));
			signMap = maintainSigns.stream().collect(Collectors.groupingBy(t -> t.getUserId(), Collectors.collectingAndThen(Collectors.toList(), t -> t.get(0))));
		}

		ElevatorSimpleInfoVO elevatorSimpleInfo = maintainPlanContentService.selectPlanElevatorInfo(planId);
		for (MapUserInfoDetailVO userInfo : details) {
			if (signMap.containsKey(userInfo.getUserId())) {
				// 用户已到达
				MaintainSign maintainSign = signMap.get(userInfo.getUserId());
				userInfo.setArrived(Boolean.TRUE);
				userInfo.setCreateTime(maintainSign.getSignTime());
				userInfo.setLongitude(new BigDecimal(maintainSign.getSignLongitude()));
				userInfo.setLatitude(new BigDecimal(maintainSign.getSignLatitude()));
			} else {
				userInfo.setArrived(Boolean.FALSE);
				if (Func.isNotEmpty(elevatorSimpleInfo)) {
					userInfo.setTargetLatitude(elevatorSimpleInfo.getLatitude());
					userInfo.setTargetLongitude(elevatorSimpleInfo.getLongitude());
					if (elevatorSimpleInfo.getLongitude() != null && elevatorSimpleInfo.getLatitude() != null && userInfo.getLongitude() != null && userInfo.getLatitude() != null) {
						int range = MapUtil.getDistance(elevatorSimpleInfo.getLongitude().doubleValue(), elevatorSimpleInfo.getLatitude().doubleValue(), userInfo.getLongitude().doubleValue(), userInfo.getLatitude().doubleValue());
						userInfo.setDistance(range);
					}
				}
			}
		}

		return details;
	}

	@Override
	public MapUserInfoDetailVO tlwPlanRescueUserCoordinate(Long planId) {
		List<MapUserInfoDetailVO> mapUserDetails = this.mapUserDetails(planId);
		return Func.isNotEmpty(mapUserDetails) ? mapUserDetails.get(0) : null;
	}

	@Override
	public Map<String, Object> mapBuildingDetailElevatorList(Long buildingId, String keyword) {

		Map<String, Object> resultMap = new HashMap<>();

		List<RescueBuildingElevatorListVO> list = baseMapper.selectBuildingElevatorList(buildingId, keyword);
		if (Func.isNotEmpty(list)) {
			List<Long> elevatorIds = list.stream().map(RescueBuildingElevatorListVO::getElevatorId).collect(Collectors.toList());
			// 查询电梯设备状态
			Map<Long, DeviceStatusVO> deviceStatusMap = estateTlwScreenService.getDeviceStatus(elevatorIds);
			for (RescueBuildingElevatorListVO elevatorListVO : list) {
				if (Func.isNotEmpty(elevatorListVO.getRescueList())) {
					elevatorListVO.getRescueList().forEach(e -> {
						e.setElevatorAddress(elevatorListVO.getElevatorAddress());
					});
				}
				if (!deviceStatusMap.containsKey(elevatorListVO.getElevatorId())) {
					continue;
				}
				DeviceStatusVO deviceStatus = this.getVideoDeviceStatus(deviceStatusMap.get(elevatorListVO.getElevatorId()));
				elevatorListVO.setDeviceId(deviceStatus.getDeviceId());
				elevatorListVO.setDeviceStatus(deviceStatus.getDeviceStatus());

				if (Func.isNotEmpty(elevatorListVO.getRescueList())) {
					elevatorListVO.setRescueList(elevatorListVO.getRescueList().stream().sorted(Comparator.comparing(RescueBuildingElevatorPlanListVO::getCreateTime).reversed()).collect(Collectors.toList()));
				}
			}
		}
		resultMap.put("elevatorList", list);

		List<RescueBuildingElevatorPlanListVO> rescueList = baseMapper.selectRescueElevatorEmptyList(buildingId);
		resultMap.put("rescueList", rescueList);
		return resultMap;
	}

	@Override
	public RescueOrderDetail rescueDetail(Long planId) {
		return baseMapper.rescueDetail(planId);
	}

	@Override
	public List<RescueOrderDetail> rescueDetailList() {
		return baseMapper.rescueDetailList();
	}

	@Override
	public TlwRescueDetailVO tlwRescueDetail(String tlwJoinId) {
		return baseMapper.tlwRescueDetail(tlwJoinId);
	}

	@Override
	public List<MaintainBuildingListVO> maintainBuildingList(String search) {
		List<MaintainBuildingListVO> list = baseMapper.maintainBuildingList();
		if (Func.isNotBlank(search)) {
			return list.stream().filter(t -> t.getName() != null && t.getName().contains(search)).collect(Collectors.toList());
		}
		return list;
	}

	@Override
	public List<EffectiveBuildingVO> effectiveBuildingList(String buildingName, String address, String phone, boolean isOnlyShowRescue) {
		List<EffectiveBuildingVO> list = baseMapper.effectiveBuildingList(buildingName, address, phone, isOnlyShowRescue);
		if (Func.isEmpty(list)) {
			return list;
		}

		List<Dict> dictList = DictCache.getList(PROJECT_TYPE_CODE);
		Map<String, String> dictMap = dictList.stream().collect(Collectors.toMap(Dict::getDictKey, Dict::getDictValue));
		for (EffectiveBuildingVO buildingVO : list) {
			buildingVO.setTypeName(dictMap.get(buildingVO.getType()));
		}
		return list;
	}

	@Override
	public List<EffectiveElevatorVO> effectiveElevatorList(String buildingId, String elevatorNum, boolean isOnlyShowRescue) {
		List<EffectiveElevatorVO> list = baseMapper.effectiveElevatorList(buildingId, elevatorNum, isOnlyShowRescue);
		if (Func.isEmpty(list)) {
			return list;
		}

		List<Dict> dictList = DictCache.getList(PROJECT_TYPE_CODE);
		Map<String, String> dictMap = dictList.stream().collect(Collectors.toMap(Dict::getDictKey, Dict::getDictValue));
		for (EffectiveElevatorVO elevatorVO : list) {
			elevatorVO.setTypeName(dictMap.get(elevatorVO.getType()));
		}
		return list;
	}

	@Override
	public List<MapRescueBuildingListVO> mapRescueBuildingList(String search, boolean isOnlyShowRescue) {
		// 在保项目列表
		List<MaintainBuildingListVO> list = baseMapper.maintainBuildingList();
		if (Func.isNotBlank(search)) {
			list = list.stream().filter(t -> t.getName() != null && t.getName().contains(search)).collect(Collectors.toList());
		}
		if (isOnlyShowRescue) {
			list = list.stream().filter(t -> t.getRescueCount() != null && t.getRescueCount() > 0).collect(Collectors.toList());
		}
		list = list.stream().filter(e -> ObjectUtil.isNotEmpty(e.getLongitude()) && ObjectUtil.isNotEmpty(e.getLatitude())).collect(Collectors.toList());
		// 在职人员列表
		TenantConfig tenantConfig = tenantConfigService.getTenantConfig(AuthUtil.getTenantId());
		String screenRoles = tenantConfig.getScreenRoles();
		List<MapUserInfoVO> userInfoVOList = baseMapper.mapUserList(screenRoles, null);
		List<MapRescueBuildingListVO> resultList = new ArrayList<>();

		list.forEach(e -> {
			MapRescueBuildingListVO result = new MapRescueBuildingListVO();
			result.setBuildingId(e.getBuildingId());
			result.setBuildingName(e.getName());
			result.setBuildingAddress(e.getAddress());
			result.setLatitude(e.getLatitude());
			result.setLongitude(e.getLongitude());
			result.setElevatorCount(e.getElevatorCount());
			result.setRescueCount(e.getRescueCount());
			result.setCityCode(e.getCityCode());
			result.setCityName(e.getCityName());
			result.setAdCode(e.getAdCode());
			result.setAdName(e.getAdName());

			if (Func.isNotEmpty(userInfoVOList)) {
				MapUserInfoVO tempUserInfo = userInfoVOList.get(0);
				int minDistance = MapUtil.getDistance(e.getLongitude().doubleValue(), e.getLatitude().doubleValue(), Double.parseDouble(tempUserInfo.getLongitude()), Double.parseDouble(tempUserInfo.getLatitude()));
				for (MapUserInfoVO mapUserInfoVO : userInfoVOList) {
					int distance = MapUtil.getDistance(e.getLongitude().doubleValue(), e.getLatitude().doubleValue(), Double.parseDouble(mapUserInfoVO.getLongitude()), Double.parseDouble(mapUserInfoVO.getLatitude()));
					if (distance < minDistance) {
						minDistance = distance;
						tempUserInfo = mapUserInfoVO;
						if (distance <= 6000) {
							break;
						}
					}
				}

				result.setDistance(minDistance);
				result.setUserId(tempUserInfo.getUserId());
				result.setUserName(tempUserInfo.getRealName());
				result.setVehicle(tempUserInfo.getVehicle());
				if (minDistance <= 6000) {
					result.setRescueLevel(5);
				} else if (minDistance <= 20000) {
					result.setRescueLevel(3);
				} else if (minDistance <= 30000) {
					result.setRescueLevel(2);
				} else {
					result.setRescueLevel(1);
				}
			}

			resultList.add(result);
		});
		return resultList;
	}

	@Override
	public MapRescueBuildingDetailVO mapRescueBuildingDetail(Long buildingId) {
		MapRescueBuildingDetailVO detailVO = buildingService.selectMapRescueBuildingDetail(buildingId);
		if (Func.isEmpty(detailVO)) {
			return null;
		}
		List<RescueBuildingElevatorPlanListVO> rescueList = baseMapper.selectRescueElevatorEmptyList(buildingId);
		detailVO.setRescueCount(Func.isNotEmpty(detailVO.getRescueCount()) ? detailVO.getRescueCount() + rescueList.size() : rescueList.size());
		return detailVO;
	}

	@Override
	public Map<String, Object> rescueProjectScreen() {
		List<RescueProjectScreenVO> list = baseMapper.rescueProjectScreen();
		if (Func.isNotEmpty(list)) {
			List<UserTrajectory> trajectorieList = userTrajectoryService.getUserNewTrajectoryList();
			Map<Long, UserTrajectory> trajectoryMap = trajectorieList.stream().collect(Collectors.toMap(t -> t.getUserId(), t -> t));
			for (RescueProjectScreenVO item : list) {
				for (RescueUserInfoVO rescueUserInfo : item.getRescueUserInfos()) {
					if (trajectoryMap.containsKey(rescueUserInfo.getUserId())) {
						UserTrajectory trajectory = trajectoryMap.get(rescueUserInfo.getUserId());
						// 计算项目与人的距离
						int distance = MapUtil.getDistance(Double.valueOf(trajectory.getLongitude()), Double.valueOf(trajectory.getLatitude()), item.getLongitude().doubleValue(), item.getLatitude().doubleValue());
						rescueUserInfo.setDistance(distance);
					}
				}
			}
		}

		Map<String, Object> map = new HashMap<>(4);
		long rescueTotal = list.stream().count();
		long timeout = list.stream().filter(t -> t.getWorkTimeInterval() > 30).count();
		map.put("rescueTotal", rescueTotal);
		map.put("timeout", timeout);
		map.put("list", list);
		return map;
	}

	@Override
	public List<MapUserInfoVO> mapRescueBuildingRecentlyUser(Long buildingId, List<String> vehicle, List<Integer> workStatus, Integer distance, List<Integer> isSign, Integer searchMin, Long elevatorId, List<String> skillKeys, String keyword) {
		MapRescueBuildingDetailVO detailVO = buildingService.selectMapRescueBuildingDetail(buildingId);
		// 在职人员列表
		TenantConfig tenantConfig = tenantConfigService.getTenantConfig(AuthUtil.getTenantId());
		String screenRoles = tenantConfig.getScreenRoles();
		List<MapUserInfoVO> userInfoVOList = baseMapper.mapUserList(screenRoles, keyword);
		// 筛选过滤
		userInfoVOList = this.mapUserDataFilter(userInfoVOList, vehicle, workStatus, isSign, searchMin, elevatorId, skillKeys);
		if (detailVO != null && detailVO.getLatitude() != null && detailVO.getLongitude() != null) {
			userInfoVOList.forEach(e -> e.setDistance(MapUtil.getDistance(Double.parseDouble(detailVO.getLongitude()), Double.parseDouble(detailVO.getLatitude()), Double.parseDouble(e.getLongitude()), Double.parseDouble(e.getLatitude()))));
		}
		if (Func.isNotEmpty(distance)) {
			userInfoVOList = userInfoVOList.stream().filter(e -> e.getDistance() == null || e.getDistance() <= distance).collect(Collectors.toList());
		}

		List<Long> mapUserIds = userInfoVOList.stream().map(t -> t.getUserId()).collect(Collectors.toList());
		Map<Long, Map<Integer, Long>> allUserPlanStatisticsMap = getPlanStatistics(mapUserIds);
		userInfoVOList.forEach(t -> {
			// 个人工单情况统计
			t.setPlanStatistics(allUserPlanStatisticsMap.get(t.getUserId()));
		});
		userInfoVOList = userInfoVOList.stream().sorted(Comparator.comparing(MapUserInfoVO::getDistance,
			Comparator.nullsFirst(Comparator.naturalOrder()))).collect(Collectors.toList());
		return userInfoVOList;
	}

	/**
	 * 地图人员数据筛选
	 *
	 * @param userInfoList
	 * @param vehicle      交通工具
	 * @param workStatus   工作状态
	 * @param isSign       签到
	 * @param searchMin    定位时间范围值
	 * @param skillKeys    技能等级
	 * @return
	 */
	private List<MapUserInfoVO> mapUserDataFilter(List<MapUserInfoVO> userInfoList, List<String> vehicle, List<Integer> workStatus, List<Integer> isSign, Integer searchMin, Long elevatorId, List<String> skillKeys) {
		if (Func.isNotEmpty(vehicle)) {
			userInfoList = userInfoList.stream().filter(e -> vehicle.contains(e.getVehicle())).collect(Collectors.toList());
		}
		if (Func.isNotEmpty(workStatus)) {
			userInfoList = userInfoList.stream().filter(e -> {
				for (Integer status : workStatus) {
					switch (status) {
						case 1:
							if (Func.isEmpty(e.getPlanTypes())) {
								return Boolean.TRUE;
							}
							break;
						case 2:
							if (Func.isNotEmpty(e.getPlanTypes()) && e.getPlanTypes().contains(MaintainPlanContentEnum.MAINTAIN_KR.getId())) {
								return Boolean.TRUE;
							}
							break;
						case 3:
							if (Func.isNotEmpty(e.getPlanTypes()) && e.getPlanTypes().contains(MaintainPlanContentEnum.MAINTAIN_WX.getId())) {
								return Boolean.TRUE;
							}
							break;
						case 4:
							if (Func.isNotEmpty(e.getPlanTypes()) && e.getPlanTypes().contains(MaintainPlanContentEnum.MAINTAIN_BY.getId())) {
								return Boolean.TRUE;
							}
							break;
					}
				}
				return Boolean.FALSE;
			}).collect(Collectors.toList());
		}
		if (Func.isNotEmpty(isSign) && isSign.size() == 1) {
			if (isSign.contains(1)) {
				userInfoList = userInfoList.stream().filter(e -> e.getIsSign()).collect(Collectors.toList());
			} else {
				userInfoList = userInfoList.stream().filter(e -> !e.getIsSign()).collect(Collectors.toList());
			}
		}
		if (Func.isNotEmpty(searchMin)) {
			Date tempDate = DateUtil.offsetMinute(DateUtil.date(), -searchMin);
			userInfoList = userInfoList.stream().filter(e -> DateUtil.compare(e.getCreateTime(), tempDate) >= 0).collect(Collectors.toList());
		}
		if (Func.isNotEmpty(skillKeys)) {
			userInfoList = userInfoList.stream().filter(e -> skillKeys.contains(e.getSkillKey())).collect(Collectors.toList());
		}
		if (Func.isNotEmpty(elevatorId) && elevatorId != 0) {
			// 梯组
			ElevatorPrincipalDept principalDept = elevatorPrincipalDeptService.getOne(Wrappers.<ElevatorPrincipalDept>lambdaQuery().eq(ElevatorPrincipalDept::getElevatorId, elevatorId));
			if (principalDept != null) {
				userInfoList = userInfoList.stream().map(e -> {
					if (Func.isNotBlank(e.getDeptId())) {
						List<Long> deptIds = Func.toLongList(e.getDeptId());
						if (deptIds.contains(principalDept.getDeptId())) {
							e.setElevatorGroupUser(true);
						}
					}
					return e;
				}).collect(Collectors.toList());
			}
		}

		return userInfoList;
	}

	@Override
	public List<MapUserInfoVO> mapRescueBuildingRecentlyMaintainUser(Long buildingId, List<String> vehicle, List<Integer> workStatus, Integer distance, List<Integer> isSign, Integer searchMin, Long elevatorId, List<String> skillKeys, String keyword) {
		Building rescueBuilding = buildingService.getById(buildingId);
		if (Func.isEmpty(rescueBuilding)) {
			throw new ServiceException("数据不存在！");
		}

		List<Building> buildingList = buildingService.list();
		if (Func.isNotEmpty(distance)) {
			// 过滤出附近范围的项目
			buildingList = buildingList.stream().filter(t -> {
				int range = MapUtil.getDistance(rescueBuilding.getLongitude().doubleValue(), rescueBuilding.getLatitude().doubleValue(), t.getLongitude().doubleValue(), t.getLatitude().doubleValue());
				return range <= distance;
			}).collect(Collectors.toList());
		}
		if (Func.isEmpty(buildingList)) {
			return null;
		}

		List<Long> buildingIds = buildingList.stream().map(t -> t.getId()).collect(Collectors.toList());
		// 项目附近保养人员
		List<Long> userIds = baseMapper.selectBuildingRecentlyMaintainUser(buildingIds);
		if (Func.isEmpty(userIds)) {
			return null;
		}
		// 在职人员列表
		TenantConfig tenantConfig = tenantConfigService.getTenantConfig(AuthUtil.getTenantId());
		String screenRoles = tenantConfig.getScreenRoles();
		List<MapUserInfoVO> userInfoVOList = baseMapper.mapUserList(screenRoles, keyword).stream().filter(t -> userIds.contains(t.getUserId())).collect(Collectors.toList());
		if (Func.isEmpty(userInfoVOList)) {
			return null;
		}

		// 筛选过滤
		userInfoVOList = this.mapUserDataFilter(userInfoVOList, vehicle, workStatus, isSign, searchMin, elevatorId, skillKeys);
		List<Long> mapUserIds = userInfoVOList.stream().map(t -> t.getUserId()).collect(Collectors.toList());
		Map<Long, Map<Integer, Long>> allUserPlanStatisticsMap = getPlanStatistics(mapUserIds);
		userInfoVOList.forEach(t -> {
			// 个人工单情况统计
			t.setPlanStatistics(allUserPlanStatisticsMap.get(t.getUserId()));
			// 人员距项目距离
			t.setDistance(MapUtil.getDistance(Double.parseDouble(t.getLongitude()), Double.parseDouble(t.getLatitude()), rescueBuilding.getLongitude().doubleValue(), rescueBuilding.getLatitude().doubleValue()));
		});

		return userInfoVOList;
	}

	@Override
	public List<MapUserInfoVO> mapRescueBuildingRecentlyBuildingUser(Long buildingId, List<String> vehicle, List<Integer> workStatus, Integer distance, List<Integer> isSign, Integer searchMin, Long elevatorId, List<String> skillKeys, String keyword) {
		Building rescueBuilding = buildingService.getById(buildingId);
		if (Func.isEmpty(rescueBuilding)) {
			throw new ServiceException("数据不存在！");
		}

		// 在职人员列表
		TenantConfig tenantConfig = tenantConfigService.getTenantConfig(AuthUtil.getTenantId());
		String screenRoles = tenantConfig.getScreenRoles();
		List<MapUserInfoVO> userInfoVOList = baseMapper.mapUserList(screenRoles, keyword);
		if (Func.isEmpty(userInfoVOList)) {
			return null;
		}

		if (Func.isNotEmpty(distance)) {
			// 过滤出附近范围的人员
			userInfoVOList = userInfoVOList.stream().filter(t -> {
				if (Func.isBlank(t.getResidentialLongitude()) || Func.isBlank(t.getResidentialLatitude())) {
					return false;
				}
				int range = MapUtil.getDistance(rescueBuilding.getLongitude().doubleValue(), rescueBuilding.getLatitude().doubleValue(), Double.parseDouble(t.getResidentialLongitude()), Double.parseDouble(t.getResidentialLatitude()));
				t.setDistance(range);
				return range <= distance;
			}).collect(Collectors.toList());
		}

		// 筛选过滤
		userInfoVOList = this.mapUserDataFilter(userInfoVOList, vehicle, workStatus, isSign, searchMin, elevatorId, skillKeys);
		List<Long> mapUserIds = userInfoVOList.stream().map(t -> t.getUserId()).collect(Collectors.toList());
		Map<Long, Map<Integer, Long>> allUserPlanStatisticsMap = getPlanStatistics(mapUserIds);
		userInfoVOList.forEach(t -> {
			// 个人工单情况统计
			t.setPlanStatistics(allUserPlanStatisticsMap.get(t.getUserId()));
		});

		return userInfoVOList;
	}

	@Override
	public List<MaintainBuildingListVO> mapRescueUserRecentlyBuilding(Long userId) {
		List<MaintainBuildingListVO> list = baseMapper.maintainBuildingList();
		list = list.stream().filter(e -> ObjectUtil.isNotEmpty(e.getLongitude()) && ObjectUtil.isNotEmpty(e.getLatitude())).collect(Collectors.toList());
		MapUserInfoVO userInfoVO = baseMapper.mapUserDetail(userId);
		list.forEach(e -> e.setDistance(MapUtil.getDistance(Double.parseDouble(userInfoVO.getLongitude()), Double.parseDouble(userInfoVO.getLatitude()), e.getLongitude().doubleValue(), e.getLatitude().doubleValue())));
		list = list.stream().sorted(Comparator.comparing(MaintainBuildingListVO::getDistance)).collect(Collectors.toList());
		if (list.size() > 5) {
			list = list.subList(0, 5);
		}
		return list;
	}

	@Override
	public RescueOverviewVO selectRescueOverview(SugarRescueSearchDTO searchDTO) {
		RescueOverviewVO result = new RescueOverviewVO();
		Date startDate = searchDTO.getStartDate();
		Date endDate = searchDTO.getEndDate();

		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());

		// 查询救援数据
		List<SugarRescueListVO> rescueList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueList = baseMapper.selectSugarRescueList(searchDTO);
			rescueList = rescueList.stream().filter(e -> Func.isNotEmpty(e.getElevatorId()) && elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
		}
		// 查询对比数据
		int betweenDay = Math.toIntExact(DateUtil.betweenDay(endDate, startDate, Boolean.TRUE));
		betweenDay = betweenDay == 0 ? 1 : betweenDay + 1;
		searchDTO.setStartDate(DateUtil.offsetDay(startDate, -betweenDay));
		searchDTO.setEndDate(DateUtil.offsetDay(endDate, -betweenDay));
		List<SugarRescueListVO> rescueListCrr = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueListCrr = baseMapper.selectSugarRescueList(searchDTO);
			rescueListCrr = rescueListCrr.stream().filter(e -> Func.isNotEmpty(e.getElevatorId()) && elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
		}

		// 救援总数
		long rescueCount = rescueList.stream().count();
		result.setRescueCount(rescueCount);

		// 对比总数
		long rescueCrrCount = rescueListCrr.stream().count();
		// 响应调度平均用时
		BigDecimal allocationDurationAve = BigDecimal.ZERO;
		// 到达平均时长
		BigDecimal arriveDurationAve = BigDecimal.ZERO;
		// 完成平均时长
		BigDecimal finishDurationAve = BigDecimal.ZERO;
		// 超规定时长到达
		long arriveOverTime = 0;
		// 日均救援次数
		BigDecimal dayRescueAve = BigDecimal.ZERO;
		if (rescueCount > 0) {

			// 响应调度平均用时
			double allocationDuration = rescueList.stream().mapToLong(SugarRescueListVO::getAllocateDuration).sum();
			// 毫秒转秒
			allocationDuration = BigDecimal.valueOf(allocationDuration).divide(BigDecimal.valueOf(1000), 3, RoundingMode.HALF_UP).doubleValue();
			allocationDurationAve = BigDecimal.valueOf(allocationDuration).divide(BigDecimal.valueOf(rescueCount), 0, RoundingMode.HALF_UP);
			result.setAllocationDurationAve(allocationDurationAve);

			// 到达平均时长
			double arriveDuration = rescueList.stream().mapToLong(SugarRescueListVO::getArriveDuration).sum();
			// 毫秒转分钟
			arriveDuration = BigDecimal.valueOf(arriveDuration).divide(BigDecimal.valueOf(60000), 3, RoundingMode.HALF_UP).doubleValue();
			arriveDurationAve = BigDecimal.valueOf(arriveDuration).divide(BigDecimal.valueOf(rescueCount), 1, RoundingMode.HALF_UP);
			result.setArriveDurationAve(arriveDurationAve);

			// 完成平均时长
			double finishDuration = rescueList.stream().mapToLong(SugarRescueListVO::getWorkMill).sum();
			// 毫秒转分钟
			finishDuration = BigDecimal.valueOf(finishDuration).divide(BigDecimal.valueOf(60000), 3, RoundingMode.HALF_UP).doubleValue();
			finishDurationAve = BigDecimal.valueOf(finishDuration).divide(BigDecimal.valueOf(rescueCount), 1, RoundingMode.HALF_UP);
			result.setFinishDurationAve(finishDurationAve);

			// 超规定时长到达
			arriveOverTime = rescueList.stream().filter(e -> e.getIsOvertime() == 1).count();
			result.setArriveOverTime(arriveOverTime);

			// 日均救援次数
			if (betweenDay > 0) {
				dayRescueAve = BigDecimal.valueOf(rescueCount).divide(BigDecimal.valueOf(betweenDay), 1, RoundingMode.HALF_UP);
			}
			result.setDayRescueAve(dayRescueAve);

		}

		// 响应调度平均用时
		double allocationDurationCrrAve = 0;
		// 到达平均时长
		double arriveDurationAveCrr = 0;
		// 完成平均时长
		double finishDurationAveCrr = 0;
		// 超规定时长到达
		double arriveOverTimeCrr = 0;
		// 日均救援次数
		BigDecimal dayRescueAveCrr = BigDecimal.ZERO;
		if (rescueCrrCount > 0) {
			if (rescueCount > 0) {
				result.setRescueCountCrr(BigDecimal.valueOf(rescueCount).subtract(BigDecimal.valueOf(rescueCrrCount)).divide(BigDecimal.valueOf(rescueCrrCount), 3, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
				// 对比数据响应调度平均用时
				double allocationDurationCrr = rescueListCrr.stream().mapToLong(SugarRescueListVO::getAllocateDuration).sum();
				// 毫秒转秒
				allocationDurationCrr = BigDecimal.valueOf(allocationDurationCrr).divide(BigDecimal.valueOf(1000), 3, RoundingMode.HALF_UP).doubleValue();
				allocationDurationCrrAve = BigDecimal.valueOf(allocationDurationCrr).divide(BigDecimal.valueOf(rescueCrrCount), 3, RoundingMode.HALF_UP).doubleValue();
				if (allocationDurationCrrAve > 0) {
					result.setAllocationDurationAveCrr(allocationDurationAve.subtract(BigDecimal.valueOf(allocationDurationCrrAve)).divide(BigDecimal.valueOf(allocationDurationCrrAve), 3, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
				} else {
					result.setAllocationDurationAveCrr(allocationDurationAve.multiply(BigDecimal.valueOf(100)));
				}

				// 对比数据到达平均时长
				double arriveDurationCrr = rescueListCrr.stream().mapToLong(SugarRescueListVO::getArriveDuration).sum();
				// 毫秒转分钟
				arriveDurationCrr = BigDecimal.valueOf(arriveDurationCrr).divide(BigDecimal.valueOf(60000), 3, RoundingMode.HALF_UP).doubleValue();
				arriveDurationAveCrr = BigDecimal.valueOf(arriveDurationCrr).divide(BigDecimal.valueOf(rescueCrrCount), 3, RoundingMode.HALF_UP).doubleValue();
				if (arriveDurationAveCrr > 0) {
					result.setArriveDurationAveCrr(arriveDurationAve.subtract(BigDecimal.valueOf(arriveDurationAveCrr)).divide(BigDecimal.valueOf(arriveDurationAveCrr), 3, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
				} else {
					result.setArriveDurationAveCrr(arriveDurationAve.multiply(BigDecimal.valueOf(100)));
				}

				// 对比数据完成平均时长
				double finishDurationCrr = rescueListCrr.stream().mapToLong(SugarRescueListVO::getWorkMill).sum();
				// 毫秒转分钟
				finishDurationCrr = BigDecimal.valueOf(finishDurationCrr).divide(BigDecimal.valueOf(60000), 3, RoundingMode.HALF_UP).doubleValue();
				finishDurationAveCrr = BigDecimal.valueOf(finishDurationCrr).divide(BigDecimal.valueOf(rescueCrrCount), 3, RoundingMode.HALF_UP).doubleValue();
				if (finishDurationAveCrr > 0) {
					result.setFinishDurationAveCrr(finishDurationAve.subtract(BigDecimal.valueOf(finishDurationAveCrr)).divide(BigDecimal.valueOf(finishDurationAveCrr), 3, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
				} else {
					result.setFinishDurationAveCrr(finishDurationAve.multiply(BigDecimal.valueOf(100)));
				}

				// 对比超规定时长到达
				arriveOverTimeCrr = rescueListCrr.stream().filter(e -> e.getIsOvertime() == 1).count();
				if (arriveOverTimeCrr > 0) {
					result.setArriveOverTimeCrr(BigDecimal.valueOf(arriveOverTime).subtract(BigDecimal.valueOf(arriveOverTimeCrr)).divide(BigDecimal.valueOf(arriveOverTimeCrr), 3, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
				} else {
					result.setArriveOverTimeCrr(BigDecimal.valueOf(arriveOverTime).multiply(BigDecimal.valueOf(100)));
				}

				// 对比日均救援次数
				dayRescueAveCrr = BigDecimal.valueOf(rescueCrrCount).divide(BigDecimal.valueOf(betweenDay), 3, RoundingMode.HALF_UP);
				if (dayRescueAveCrr.compareTo(BigDecimal.ZERO) > 0) {
					result.setDayRescueAveCrr(dayRescueAve.subtract(dayRescueAveCrr).divide(dayRescueAveCrr, 3, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
				} else {
					result.setDayRescueAveCrr(dayRescueAve.multiply(BigDecimal.valueOf(100)));
				}

			} else {
				result.setRescueCountCrr(BigDecimal.valueOf(rescueCrrCount).multiply(BigDecimal.valueOf(-100)));
				result.setAllocationDurationAveCrr(BigDecimal.valueOf(allocationDurationCrrAve).multiply(BigDecimal.valueOf(-100)));
				result.setArriveDurationAveCrr(BigDecimal.valueOf(arriveDurationAveCrr).multiply(BigDecimal.valueOf(-100)));
				result.setArriveOverTimeCrr(BigDecimal.valueOf(arriveOverTimeCrr).multiply(BigDecimal.valueOf(-100)));
				result.setFinishDurationAveCrr(BigDecimal.valueOf(finishDurationAveCrr).multiply(BigDecimal.valueOf(-100)));
				result.setDayRescueAveCrr(dayRescueAveCrr.multiply(BigDecimal.valueOf(-100)));
			}
		} else {
			result.setRescueCountCrr(BigDecimal.valueOf(rescueCount).multiply(BigDecimal.valueOf(100)));
			result.setAllocationDurationAveCrr(allocationDurationAve.multiply(BigDecimal.valueOf(100)));
			result.setArriveDurationAveCrr(arriveDurationAve.multiply(BigDecimal.valueOf(100)));
			result.setArriveOverTimeCrr(BigDecimal.valueOf(arriveOverTime).multiply(BigDecimal.valueOf(100)));
			result.setFinishDurationAveCrr(finishDurationAve.multiply(BigDecimal.valueOf(100)));
			result.setDayRescueAveCrr(dayRescueAveCrr.multiply(BigDecimal.valueOf(100)));
		}
		return result;
	}

	@Override
	public List<RescueAreaVO> selectRescueArea(SugarRescueSearchDTO searchDTO) {
		List<RescueAreaVO> result = new ArrayList<>();

		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());

		// 查询救援数据
		List<SugarRescueListVO> rescueList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueList = baseMapper.selectSugarRescueList(searchDTO);
			rescueList = rescueList.stream().filter(e -> elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
		}
		if (ObjectUtil.isNotEmpty(rescueList)) {
			Map<String, List<SugarRescueListVO>> areaMapGroup = rescueList.stream().collect(Collectors.groupingBy(SugarRescueListVO::getAdName));
			areaMapGroup.forEach((k, v) -> {
				RescueAreaVO areaVO = new RescueAreaVO();
				areaVO.setRescueCount(v.stream().count());
				areaVO.setAdName(k);
				result.add(areaVO);
			});
		}
		return result;
	}

	@Override
	public Map<Integer, Long> selectRescueHighTime(SugarRescueSearchDTO searchDTO) {

		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());

		// 查询救援数据
		List<SugarRescueListVO> rescueList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueList = baseMapper.selectSugarRescueList(searchDTO);
			rescueList = rescueList.stream().filter(e -> elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
		}
		Map<Integer, Long> result = this.initRescueHighTime();
		if (ObjectUtil.isNotEmpty(rescueList)) {
			Map<Integer, List<SugarRescueListVO>> hourMapGroup = rescueList.stream().collect(Collectors.groupingBy(e -> DateUtil.hour(e.getCreateTime(), Boolean.TRUE)));
			hourMapGroup.forEach((k, v) -> {
				result.put(k, v.stream().count());
			});
		}
		return result;
	}

	@Override
	public List<StatisticsTrendVO> selectRescueCountTrend(SugarRescueSearchDTO searchDTO) {

		Date startDate = searchDTO.getStartDate();
		Date endDate = searchDTO.getEndDate();
		Date startCrrDate = DateUtil.offsetDay(startDate, -1);
		Date endCrrDate = DateUtil.offsetDay(endDate, -1);
		if (searchDTO.getDataType().equals(2)) {
			startDate = DateUtil.beginOfWeek(startDate);
			endDate = DateUtil.endOfWeek(endDate);
			startCrrDate = DateUtil.offsetDay(startDate, -7);
			endCrrDate = DateUtil.offsetDay(endDate, -7);
		} else if (searchDTO.getDataType().equals(3)) {
			startDate = DateUtil.beginOfMonth(startDate);
			endDate = DateUtil.endOfMonth(endDate);
			startCrrDate = DateUtil.offsetDay(startDate, -30);
			endCrrDate = DateUtil.offsetDay(endDate, -30);
		}

		String[] labelArr = {"救援次数", "日环比"};
		List<StatisticsTrendVO> result = new ArrayList<>(labelArr.length);
		// 救援次数数据初始化
		StatisticsTrendVO rescueCountTrend = this.initTrendEntirety(searchDTO, labelArr[0]);
		// 环比趋势初始化
//		StatisticsTrendVO lrrTrend = this.initTrendEntirety(searchDTO,labelArr[1]);

		// 查询救援数据
		// 处理救援次数
		searchDTO.setStartDate(startDate);
		searchDTO.setEndDate(endDate);

		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());

		List<SugarRescueListVO> rescueList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueList = baseMapper.selectSugarRescueList(searchDTO);
		}
		if (ObjectUtil.isNotEmpty(rescueList)) {
			Map<String, List<SugarRescueListVO>> dateGroup;
			if (searchDTO.getDataType().equals(1)) {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(), "yyyy/M/d")));
			} else if (searchDTO.getDataType().equals(2)) {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
			} else {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(e -> String.format("%s月", DateUtil.format(e.getPlanDate(), "yyyy年M"))));
			}
			dateGroup.forEach((k, v) -> rescueCountTrend.getTrendData().put(k, v.stream().count()));
		}
		result.add(rescueCountTrend);
		// 查询对比数据
//		searchDTO.setStartDate(startCrrDate);
//		searchDTO.setEndDate(endCrrDate);
//		List<SugarRescueListVO> rescueCrrList = baseMapper.selectSugarRescueList(searchDTO);
//		StatisticsTrendVO rescueCountCrrTrend = this.initTrendEntirety(searchDTO,labelArr[0]);
//		if(ObjectUtil.isNotEmpty(rescueCrrList)){
//			Map<String,List<SugarRescueListVO>> dateGroup;
//			if(searchDTO.getDataType().equals(1)){
//				dateGroup = rescueCrrList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(),"yyyy/M/d")));
//			}else if(searchDTO.getDataType().equals(2)){
//				dateGroup = rescueCrrList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s",DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
//			}else{
//				dateGroup = rescueCrrList.stream().collect(Collectors.groupingBy(e -> String.format("%s月",DateUtil.format(e.getPlanDate(), "yyyy年M"))));
//			}
//			dateGroup.forEach((k,v) -> rescueCountCrrTrend.getTrendData().put(k,v.stream().count()));
//		}

		// 根据救援次数趋势结果获取环比趋势
//		rescueCountTrend.getTrendData().forEach((k,v) -> {
//			long vCrr;
//			if(searchDTO.getDataType().equals(1)){
//				vCrr = Long.parseLong(String.valueOf(rescueCountCrrTrend.getTrendData().get(DateUtil.format(DateUtil.offsetDay(DateUtil.parseDate(k),-1),"yyyy/M/d"))));
//			}else if(searchDTO.getDataType().equals(2)) {
//				String[] dateArr = k.split("~");
//				Date startCrrKeyDate = DateUtil.parseDate(dateArr[0]);
//				Date endCrrKeyDate = DateUtil.parseDate(dateArr[1]);
//				vCrr = Long.parseLong(String.valueOf(rescueCountCrrTrend.getTrendData().get(String.format("%s~%s",DateUtil.format(DateUtil.offsetDay(startCrrKeyDate,-7), "yyyy/M/d"), DateUtil.format(DateUtil.offsetDay(endCrrKeyDate,-7), "yyyy/M/d")))));
//			}else {
//				vCrr = Long.parseLong(String.valueOf(rescueCountCrrTrend.getTrendData().get(String.format("%s月",DateUtil.format(DateUtil.offsetMonth(DateUtil.parseDate(k),-1), "yyyy年M")))));
//			}
//			if(vCrr > 0){
//				if(Long.parseLong(String.valueOf(v)) > 0){
//					lrrTrend.getTrendData().put(k,BigDecimal.valueOf(Long.parseLong(String.valueOf(v))).subtract(BigDecimal.valueOf(vCrr)).divide(BigDecimal.valueOf(vCrr),3,RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
//				}else {
//					lrrTrend.getTrendData().put(k,BigDecimal.valueOf(vCrr).multiply(BigDecimal.valueOf(-100)));
//				}
//			}else {
//				lrrTrend.getTrendData().put(k,BigDecimal.valueOf(Long.parseLong(String.valueOf(v))).multiply(BigDecimal.valueOf(100)));
//			}
//		});
//		result.add(lrrTrend);

		return result;
	}

	@Override
	public List<Map<String, Object>> selectRescueArriveDurationRadar(SugarRescueSearchDTO searchDTO) {
//		String[] labelArr = {"5min以内","5~10min","10~15min","15~20min","20~25min","25~30min","超30min"};
		// 初始化数据
		String[] labelArr = {"5min以内", "5~10min", "10~15min", "15~20min", "20~25min", "25~30min", "超30min"};
		List<Map<String, Object>> result = new ArrayList<>(labelArr.length);
		for (String label : labelArr) {
			Map<String, Object> map = new HashMap<>(3);
			map.put("label", label);
			map.put("num", 0);
			map.put("pro", BigDecimal.ZERO);
			result.add(map);
		}
		// 查询救援数据
		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
		List<SugarRescueListVO> rescueList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueList = baseMapper.selectSugarRescueList(searchDTO);
			rescueList = rescueList.stream().filter(e -> elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
		}
		if (ObjectUtil.isNotEmpty(rescueList)) {
			// 总数
			long planTotal = rescueList.stream().count();
			List<SugarRescueListVO> finalRescueList = rescueList;
			result.forEach(e -> {
				long num = 0;
				if (String.valueOf(e.get("label")).equals(labelArr[0])) {
					num = finalRescueList.stream().filter(j -> j.getArriveDuration() <= 5 * 60 * 1000).count();
				} else if (String.valueOf(e.get("label")).equals(labelArr[1])) {
					num = finalRescueList.stream().filter(j -> j.getArriveDuration() <= 10 * 60 * 1000 && j.getArriveDuration() > 5 * 60 * 1000).count();
				} else if (String.valueOf(e.get("label")).equals(labelArr[2])) {
					num = finalRescueList.stream().filter(j -> j.getArriveDuration() <= 15 * 60 * 1000 && j.getArriveDuration() > 10 * 60 * 1000).count();
				} else if (String.valueOf(e.get("label")).equals(labelArr[3])) {
					num = finalRescueList.stream().filter(j -> j.getArriveDuration() <= 20 * 60 * 1000 && j.getArriveDuration() > 15 * 60 * 1000).count();
				} else if (String.valueOf(e.get("label")).equals(labelArr[4])) {
					num = finalRescueList.stream().filter(j -> j.getArriveDuration() <= 25 * 60 * 1000 && j.getArriveDuration() > 20 * 60 * 1000).count();
				} else if (String.valueOf(e.get("label")).equals(labelArr[5])) {
					num = finalRescueList.stream().filter(j -> j.getArriveDuration() <= 30 * 60 * 1000 && j.getArriveDuration() > 25 * 60 * 1000).count();
				} else if (String.valueOf(e.get("label")).equals(labelArr[6])) {
					num = finalRescueList.stream().filter(j -> j.getArriveDuration() > 30 * 60 * 1000).count();
				}
				e.put("num", num);
				e.put("pro", BigDecimal.valueOf(num).divide(BigDecimal.valueOf(planTotal), 3, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
			});
		}
		return result;
	}

	@Override
	public Map<String, Object> selectRescueArriveDurationDetail(SugarRescueSearchDTO searchDTO) {
		Map<String, Object> result = new HashMap<>(4);
		result.put("overtime", 0);
		result.put("overtimePro", BigDecimal.ZERO);
		result.put("onTime", 0);
		result.put("onTimePro", BigDecimal.ZERO);
		// 查询救援数据
		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
		List<SugarRescueListVO> rescueList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueList = baseMapper.selectSugarRescueList(searchDTO);
			rescueList = rescueList.stream().filter(e -> elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
		}
		if (ObjectUtil.isNotEmpty(rescueList)) {
			long planTotal = rescueList.stream().count();
			long overtime = rescueList.stream().filter(e -> e.getIsOvertime() == 1 || e.getIsOvertime() == 2).count();
			long onTime = rescueList.stream().filter(e -> e.getIsOvertime() == 3).count();
			result.put("overtime", overtime);
			result.put("overtimePro", BigDecimal.valueOf(overtime).divide(BigDecimal.valueOf(planTotal), 3, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
			result.put("onTime", onTime);
			result.put("onTimePro", BigDecimal.valueOf(onTime).divide(BigDecimal.valueOf(planTotal), 3, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
		}
		return result;
	}

	@Override
	public List<StatisticsTrendVO> selectRescueArriveDurationOvertimeTrend(SugarRescueSearchDTO searchDTO) {

		if (searchDTO.getDataType().equals(2)) {
			searchDTO.setStartDate(DateUtil.beginOfWeek(searchDTO.getStartDate()));
			searchDTO.setEndDate(DateUtil.endOfWeek(searchDTO.getEndDate()));
		}
		String[] labelArr = {"超法规时长到达", "超项目指定时长到达", "按规定时长到达", "超规定时长到达总次数"};
		List<StatisticsTrendVO> result = this.initTrendEntirety(searchDTO, labelArr);
		// 查询救援数据
		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
		List<SugarRescueListVO> rescueList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueList = baseMapper.selectSugarRescueList(searchDTO);
			rescueList = rescueList.stream().filter(e -> elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
		}
		if (ObjectUtil.isNotEmpty(rescueList)) {
			Map<String, List<SugarRescueListVO>> dateGroup;
			if (searchDTO.getDataType().equals(1)) {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(), "yyyy/M/d")));
			} else {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
			}
			result.forEach(e -> dateGroup.forEach((k, v) -> {
				long num;
				if (e.getType().equals(labelArr[0])) {
					num = v.stream().filter(j -> j.getIsOvertime() == 1).count();
				} else if (e.getType().equals(labelArr[1])) {
					num = v.stream().filter(j -> j.getIsOvertime() == 2).count();
				} else if (e.getType().equals(labelArr[2])) {
					num = v.stream().filter(j -> j.getIsOvertime() == 3).count();
				} else {
					num = v.stream().filter(j -> j.getIsOvertime() == 1 || j.getIsOvertime() == 2).count();
				}
				e.getTrendData().put(k, num);
			}));
		}

		return result;
	}

	@Override
	public Map<String, Object> selectRescueAllocateDurationTrend(SugarRescueSearchDTO searchDTO) {

		Date startDate = searchDTO.getStartDate();
		Date endDate = searchDTO.getEndDate();
//		Date startCrrDate = DateUtil.offsetDay(startDate,-1);
//		Date endCrrDate = DateUtil.offsetDay(endDate,-1);
		if (searchDTO.getDataType().equals(2)) {
			startDate = DateUtil.beginOfWeek(startDate);
			endDate = DateUtil.endOfWeek(endDate);
//			startCrrDate = DateUtil.offsetDay(startDate,-7);
//			endCrrDate = DateUtil.offsetDay(endDate,-7);
		}

		Map<String, Object> resultMap = new HashMap<>();
		resultMap.put("max", 0);
		resultMap.put("med", 0);
		resultMap.put("ave", 0);

		String[] labelArr = {"最高值", "中位数", "平均值"};

		searchDTO.setStartDate(startDate);
		searchDTO.setEndDate(endDate);
		List<StatisticsTrendVO> result = this.initTrendEntirety(searchDTO, labelArr);

		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
		List<SugarRescueListVO> rescueList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueList = baseMapper.selectSugarRescueList(searchDTO);
			rescueList = rescueList.stream().filter(e -> elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
		}
		if (ObjectUtil.isNotEmpty(rescueList)) {

			rescueList.sort(Comparator.comparing(SugarRescueListVO::getAllocateDuration));

			// 最高值
			long max = rescueList.stream().mapToLong(SugarRescueListVO::getAllocateDuration).max().orElse(0);
			resultMap.put("max", BigDecimal.valueOf(max).divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP));
			// 中位数
			long med = 0;
			long allPlanTotal = rescueList.stream().count();
			if (allPlanTotal > 0) {
				int index = (int) (allPlanTotal / 2);
				if (allPlanTotal % 2 == 0) {
					med = BigDecimal.valueOf(rescueList.get(index - 1).getAllocateDuration() + rescueList.get(index).getAllocateDuration()).divide(BigDecimal.valueOf(2), 1, RoundingMode.HALF_UP).longValue();
				} else {
					med = rescueList.get(index).getAllocateDuration();
				}
			}
			resultMap.put("med", BigDecimal.valueOf(med).divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP));
			// 平均数
			double ave = rescueList.stream().mapToLong(SugarRescueListVO::getAllocateDuration).average().orElse(0);
			resultMap.put("ave", BigDecimal.valueOf(ave).divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP));

			Map<String, List<SugarRescueListVO>> dateGroup;
			if (searchDTO.getDataType().equals(1)) {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(), "yyyy/M/d")));
			} else {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
			}
			result.forEach(e -> dateGroup.forEach((k, v) -> {
				long num = 0;
				if (e.getType().equals(labelArr[0])) {
					num = v.stream().mapToLong(SugarRescueListVO::getAllocateDuration).max().orElse(0);
				} else if (e.getType().equals(labelArr[1])) {
					long planTotal = v.stream().count();
					if (planTotal > 0) {
						int index = (int) (planTotal / 2);
						if (planTotal % 2 == 0) {
							num = BigDecimal.valueOf(v.get(index - 1).getAllocateDuration() + v.get(index).getAllocateDuration()).divide(BigDecimal.valueOf(2), 1, RoundingMode.HALF_UP).longValue();
						} else {
							num = v.get(index).getAllocateDuration();
						}
					}
				} else if (e.getType().equals(labelArr[2])) {
					long planTotal = v.stream().count();
					long sum = v.stream().mapToLong(SugarRescueListVO::getAllocateDuration).sum();
					num = BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(planTotal), 1, RoundingMode.HALF_UP).longValue();
				}
				num = BigDecimal.valueOf(num).divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP).longValue();
				e.getTrendData().put(k, num);
			}));
		}

		resultMap.put("trend", result);

//		String[] labelArr = {"最高值","平均值","最低值","平均值日环比"};
//		searchDTO.setStartDate(startDate);
//		searchDTO.setEndDate(endDate);
//		List<StatisticsTrendVO> result = this.initTrendEntirety(searchDTO,labelArr);
//		List<SugarRescueListVO> rescueList = baseMapper.selectSugarRescueList(searchDTO);
//		if(ObjectUtil.isNotEmpty(rescueList)){
//			Map<String,List<SugarRescueListVO>> dateGroup;
//			if(searchDTO.getDataType().equals(1)){
//				dateGroup = rescueList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(),"yyyy/M/d")));
//			}else {
//				dateGroup = rescueList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s",DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
//			}
//			result.forEach(e -> dateGroup.forEach((k, v) -> {
//				long num = 0;
//				if(e.getType().equals(labelArr[0])){
//					num = v.stream().mapToLong(SugarRescueListVO::getAllocateDuration).max().orElse(0);
//				}else if(e.getType().equals(labelArr[1])) {
//					long planTotal = v.stream().count();
//					long sum = v.stream().mapToLong(SugarRescueListVO::getAllocateDuration).sum();
//					num = BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(planTotal),1,RoundingMode.HALF_UP).longValue();
//				}else if(e.getType().equals(labelArr[2])) {
//					num = v.stream().mapToLong(SugarRescueListVO::getAllocateDuration).min().orElse(0);
//				}
//				num = BigDecimal.valueOf(num).divide(BigDecimal.valueOf(1000),1,RoundingMode.HALF_UP).longValue();
//				e.getTrendData().put(k,num);
//			}));
//		}

//		searchDTO.setStartDate(startCrrDate);
//		searchDTO.setEndDate(endCrrDate);
//		List<SugarRescueListVO> rescueCrrList = baseMapper.selectSugarRescueList(searchDTO);
//		if(ObjectUtil.isNotEmpty(rescueCrrList)){
//			Map<String,List<SugarRescueListVO>> dateCrrGroup;
//			if(searchDTO.getDataType().equals(1)){
//				dateCrrGroup = rescueCrrList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(),"yyyy/M/d")));
//			}else {
//				dateCrrGroup = rescueCrrList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s",DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
//			}
//			// 获取平均值结果
//			result.forEach(e -> {
//				if(e.getType().equals(labelArr[3])){
//					StatisticsTrendVO trendVO = result.get(1);
//					e.getTrendData().forEach((k,v) -> {
//						String kCrr;
//						if(searchDTO.getDataType().equals(1)){
//							kCrr = DateUtil.format(DateUtil.offsetDay(DateUtil.parseDate(k),-1),"yyyy/M/d");
//						}else {
//							String[] dateArr = k.split("~");
//							Date startCrrKeyDate = DateUtil.parseDate(dateArr[0]);
//							Date endCrrKeyDate = DateUtil.parseDate(dateArr[1]);
//							kCrr = String.format("%s~%s",DateUtil.format(DateUtil.offsetDay(startCrrKeyDate,-7), "yyyy/M/d"), DateUtil.format(DateUtil.offsetDay(endCrrKeyDate,-7), "yyyy/M/d"));
//						}
//						long num = Long.parseLong(String.valueOf(trendVO.getTrendData().get(k)));
//						if(dateCrrGroup.containsKey(kCrr)){
//							long planTotalCrr = dateCrrGroup.get(kCrr).stream().count();
//							long sumCrr = dateCrrGroup.get(kCrr).stream().mapToLong(SugarRescueListVO::getAllocateDuration).sum();
//							long numCrr = BigDecimal.valueOf(sumCrr).divide(BigDecimal.valueOf(planTotalCrr).multiply(BigDecimal.valueOf(1000)),3,RoundingMode.HALF_UP).longValue();
//							if(numCrr > 0){
//								if(num > 0){
//									e.getTrendData().put(k,BigDecimal.valueOf(num).subtract(BigDecimal.valueOf(numCrr)).divide(BigDecimal.valueOf(numCrr),3,RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
//								}else {
//									e.getTrendData().put(k,BigDecimal.valueOf(numCrr).multiply(BigDecimal.valueOf(-100)));
//								}
//							}else {
//								e.getTrendData().put(k,BigDecimal.valueOf(num).multiply(BigDecimal.valueOf(100)));
//							}
//						}else {
//							e.getTrendData().put(k,BigDecimal.valueOf(num).multiply(BigDecimal.valueOf(100)));
//						}
//					});
//				}
//			});
//		}

		return resultMap;
	}

	@Override
	public Map<String, Object> selectRescueArriveDurationTrend(SugarRescueSearchDTO searchDTO) {
		Date startDate = searchDTO.getStartDate();
		Date endDate = searchDTO.getEndDate();
//		Date startCrrDate = DateUtil.offsetDay(startDate,-1);
//		Date endCrrDate = DateUtil.offsetDay(endDate,-1);
		if (searchDTO.getDataType().equals(2)) {
			startDate = DateUtil.beginOfWeek(startDate);
			endDate = DateUtil.endOfWeek(endDate);
//			startCrrDate = DateUtil.offsetDay(startDate,-7);
//			endCrrDate = DateUtil.offsetDay(endDate,-7);
		}

		Map<String, Object> resultMap = new HashMap<>();
		resultMap.put("max", 0);
		resultMap.put("med", 0);
		resultMap.put("ave", 0);

		String[] labelArr = {"最高值", "中位数", "平均值"};

		searchDTO.setStartDate(startDate);
		searchDTO.setEndDate(endDate);
		List<StatisticsTrendVO> result = this.initTrendEntirety(searchDTO, labelArr);
		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
		List<SugarRescueListVO> rescueList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueList = baseMapper.selectSugarRescueList(searchDTO);
			rescueList = rescueList.stream().filter(e -> elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
		}
		if (ObjectUtil.isNotEmpty(rescueList)) {

			rescueList.sort(Comparator.comparing(SugarRescueListVO::getArriveDuration));

			// 最高值
			long max = rescueList.stream().mapToLong(SugarRescueListVO::getArriveDuration).max().orElse(0);
			resultMap.put("max", BigDecimal.valueOf(max).divide(BigDecimal.valueOf(60000), 1, RoundingMode.HALF_UP));
			// 中位数
			long med = 0;
			long allPlanTotal = rescueList.stream().count();
			if (allPlanTotal > 0) {
				int index = (int) (allPlanTotal / 2);
				if (allPlanTotal % 2 == 0) {
					med = BigDecimal.valueOf(rescueList.get(index - 1).getArriveDuration() + rescueList.get(index).getArriveDuration()).divide(BigDecimal.valueOf(2), 1, RoundingMode.HALF_UP).longValue();
				} else {
					med = rescueList.get(index).getArriveDuration();
				}
			}
			resultMap.put("med", BigDecimal.valueOf(med).divide(BigDecimal.valueOf(60000), 1, RoundingMode.HALF_UP));
			// 平均数
			double ave = rescueList.stream().mapToLong(SugarRescueListVO::getArriveDuration).average().orElse(0);
			resultMap.put("ave", BigDecimal.valueOf(ave).divide(BigDecimal.valueOf(60000), 1, RoundingMode.HALF_UP));

			Map<String, List<SugarRescueListVO>> dateGroup;
			if (searchDTO.getDataType().equals(1)) {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(), "yyyy/M/d")));
			} else {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
			}
			result.forEach(e -> dateGroup.forEach((k, v) -> {
				long num = 0;
				if (e.getType().equals(labelArr[0])) {
					num = v.stream().mapToLong(SugarRescueListVO::getArriveDuration).max().orElse(0);
				} else if (e.getType().equals(labelArr[1])) {
					long planTotal = v.stream().count();
					if (planTotal > 0) {
						int index = (int) (planTotal / 2);
						if (planTotal % 2 == 0) {
							num = BigDecimal.valueOf(v.get(index - 1).getArriveDuration() + v.get(index).getArriveDuration()).divide(BigDecimal.valueOf(2), 1, RoundingMode.HALF_UP).longValue();
						} else {
							num = v.get(index).getArriveDuration();
						}
					}
				} else if (e.getType().equals(labelArr[2])) {
					long planTotal = v.stream().count();
					long sum = v.stream().mapToLong(SugarRescueListVO::getArriveDuration).sum();
					num = BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(planTotal), 1, RoundingMode.HALF_UP).longValue();
				}
				e.getTrendData().put(k, BigDecimal.valueOf(num).divide(BigDecimal.valueOf(60000), 1, RoundingMode.HALF_UP));
			}));
		}

		resultMap.put("trend", result);

//		String[] labelArr = {"最高值","平均值","最低值","平均值日环比"};
//
//		searchDTO.setStartDate(startDate);
//		searchDTO.setEndDate(endDate);
//		List<StatisticsTrendVO> result = this.initTrendEntirety(searchDTO,labelArr);
//		List<SugarRescueListVO> rescueList = baseMapper.selectSugarRescueList(searchDTO);
//		if(ObjectUtil.isNotEmpty(rescueList)){
//			Map<String,List<SugarRescueListVO>> dateGroup;
//			if(searchDTO.getDataType().equals(1)){
//				dateGroup = rescueList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(),"yyyy/M/d")));
//			}else {
//				dateGroup = rescueList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s",DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
//			}
//			result.forEach(e -> dateGroup.forEach((k, v) -> {
//				long num = 0;
//				if(e.getType().equals(labelArr[0])){
//					num = v.stream().mapToLong(SugarRescueListVO::getArriveDuration).max().orElse(0);
//				}else if(e.getType().equals(labelArr[1])) {
//					long planTotal = v.stream().count();
//					long sum = v.stream().mapToLong(SugarRescueListVO::getArriveDuration).sum();
//					num = BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(planTotal),1,RoundingMode.HALF_UP).longValue();
//				}else if(e.getType().equals(labelArr[2])) {
//					num = v.stream().mapToLong(SugarRescueListVO::getArriveDuration).min().orElse(0);
//				}
//				e.getTrendData().put(k,BigDecimal.valueOf(num).divide(BigDecimal.valueOf(60000),1,RoundingMode.HALF_UP));
//			}));
//		}
//
//		searchDTO.setStartDate(startCrrDate);
//		searchDTO.setEndDate(endCrrDate);
//		List<SugarRescueListVO> rescueCrrList = baseMapper.selectSugarRescueList(searchDTO);
//		if(ObjectUtil.isNotEmpty(rescueCrrList)){
//			Map<String,List<SugarRescueListVO>> dateCrrGroup;
//			if(searchDTO.getDataType().equals(1)){
//				dateCrrGroup = rescueCrrList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(),"yyyy/M/d")));
//			}else {
//				dateCrrGroup = rescueCrrList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s",DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
//			}
//			// 获取平均值结果
//			result.forEach(e -> {
//				if(e.getType().equals(labelArr[3])){
//					StatisticsTrendVO trendVO = result.get(1);
//					e.getTrendData().forEach((k,v) -> {
//						String kCrr;
//						if(searchDTO.getDataType().equals(1)){
//							kCrr = DateUtil.format(DateUtil.offsetDay(DateUtil.parseDate(k),-1),"yyyy/M/d");
//						}else {
//							String[] dateArr = k.split("~");
//							Date startCrrKeyDate = DateUtil.parseDate(dateArr[0]);
//							Date endCrrKeyDate = DateUtil.parseDate(dateArr[1]);
//							kCrr = String.format("%s~%s",DateUtil.format(DateUtil.offsetDay(startCrrKeyDate,-7), "yyyy/M/d"), DateUtil.format(DateUtil.offsetDay(endCrrKeyDate,-7), "yyyy/M/d"));
//						}
//						double num = Double.parseDouble(String.valueOf(trendVO.getTrendData().get(k)));
//						if(dateCrrGroup.containsKey(kCrr)){
//							long planTotalCrr = dateCrrGroup.get(kCrr).stream().count();
//							long sumCrr = dateCrrGroup.get(kCrr).stream().mapToLong(SugarRescueListVO::getArriveDuration).sum();
//							long numCrr = BigDecimal.valueOf(sumCrr).divide(BigDecimal.valueOf(planTotalCrr).multiply(BigDecimal.valueOf(60000)),3,RoundingMode.HALF_UP).longValue();
//							if(numCrr > 0){
//								if(num > 0){
//									e.getTrendData().put(k,BigDecimal.valueOf(num).subtract(BigDecimal.valueOf(numCrr)).divide(BigDecimal.valueOf(numCrr),3,RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
//								}else {
//									e.getTrendData().put(k,BigDecimal.valueOf(numCrr).multiply(BigDecimal.valueOf(-100)));
//								}
//							}else {
//								e.getTrendData().put(k,BigDecimal.valueOf(num).multiply(BigDecimal.valueOf(100)));
//							}
//						}else {
//							e.getTrendData().put(k,BigDecimal.valueOf(num).multiply(BigDecimal.valueOf(100)));
//						}
//					});
//				}
//			});
//		}

		return resultMap;
	}

	@Override
	public Map<String, Object> selectRescueWorkDurationTrend(SugarRescueSearchDTO searchDTO) {
		Date startDate = searchDTO.getStartDate();
		Date endDate = searchDTO.getEndDate();
//		Date startCrrDate = DateUtil.offsetDay(startDate,-1);
//		Date endCrrDate = DateUtil.offsetDay(endDate,-1);
		if (searchDTO.getDataType().equals(2)) {
			startDate = DateUtil.beginOfWeek(startDate);
			endDate = DateUtil.endOfWeek(endDate);
//			startCrrDate = DateUtil.offsetDay(startDate,-7);
//			endCrrDate = DateUtil.offsetDay(endDate,-7);
		}

		Map<String, Object> resultMap = new HashMap<>();
		resultMap.put("max", 0);
		resultMap.put("med", 0);
		resultMap.put("ave", 0);

		String[] labelArr = {"最高值", "中位数", "平均值"};

		searchDTO.setStartDate(startDate);
		searchDTO.setEndDate(endDate);
		List<StatisticsTrendVO> result = this.initTrendEntirety(searchDTO, labelArr);
		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
		List<SugarRescueListVO> rescueList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueList = baseMapper.selectSugarRescueList(searchDTO);
			rescueList = rescueList.stream().filter(e -> elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
		}
		if (ObjectUtil.isNotEmpty(rescueList)) {

			rescueList.sort(Comparator.comparing(SugarRescueListVO::getWorkMill));

			// 最高值
			long max = rescueList.stream().mapToLong(SugarRescueListVO::getWorkMill).max().orElse(0);
			resultMap.put("max", BigDecimal.valueOf(max).divide(BigDecimal.valueOf(60000), 1, RoundingMode.HALF_UP));
			// 中位数
			long med = 0;
			long allPlanTotal = rescueList.stream().count();
			if (allPlanTotal > 0) {
				int index = (int) (allPlanTotal / 2);
				if (allPlanTotal % 2 == 0) {
					med = BigDecimal.valueOf(rescueList.get(index - 1).getWorkMill() + rescueList.get(index).getWorkMill()).divide(BigDecimal.valueOf(2), 1, RoundingMode.HALF_UP).longValue();
				} else {
					med = rescueList.get(index).getWorkMill();
				}
			}
			resultMap.put("med", BigDecimal.valueOf(med).divide(BigDecimal.valueOf(60000), 1, RoundingMode.HALF_UP));
			// 平均数
			double ave = rescueList.stream().mapToLong(SugarRescueListVO::getWorkMill).average().orElse(0);
			resultMap.put("ave", BigDecimal.valueOf(ave).divide(BigDecimal.valueOf(60000), 1, RoundingMode.HALF_UP));

			Map<String, List<SugarRescueListVO>> dateGroup;
			if (searchDTO.getDataType().equals(1)) {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(), "yyyy/M/d")));
			} else {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
			}
			result.forEach(e -> dateGroup.forEach((k, v) -> {
				long num = 0;
				if (e.getType().equals(labelArr[0])) {
					num = v.stream().mapToLong(SugarRescueListVO::getWorkMill).max().orElse(0);
				} else if (e.getType().equals(labelArr[1])) {
					long planTotal = v.stream().count();
					if (planTotal > 0) {
						int index = (int) (planTotal / 2);
						if (planTotal % 2 == 0) {
							num = BigDecimal.valueOf(v.get(index - 1).getWorkMill() + v.get(index).getWorkMill()).divide(BigDecimal.valueOf(2), 1, RoundingMode.HALF_UP).longValue();
						} else {
							num = v.get(index).getWorkMill();
						}
					}
				} else if (e.getType().equals(labelArr[2])) {
					long planTotal = v.stream().count();
					long sum = v.stream().mapToLong(SugarRescueListVO::getWorkMill).sum();
					num = BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(planTotal), 1, RoundingMode.HALF_UP).longValue();
				}
				e.getTrendData().put(k, BigDecimal.valueOf(num).divide(BigDecimal.valueOf(60000), 1, RoundingMode.HALF_UP));
			}));
		}

		resultMap.put("trend", result);

//		String[] labelArr = {"最高值","平均值","最低值","平均值日环比"};
//
//		searchDTO.setStartDate(startDate);
//		searchDTO.setEndDate(endDate);
//		List<StatisticsTrendVO> result = this.initTrendEntirety(searchDTO,labelArr);
//		List<SugarRescueListVO> rescueList = baseMapper.selectSugarRescueList(searchDTO);
//		if(ObjectUtil.isNotEmpty(rescueList)){
//			Map<String,List<SugarRescueListVO>> dateGroup;
//			if(searchDTO.getDataType().equals(1)){
//				dateGroup = rescueList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(),"yyyy/M/d")));
//			}else {
//				dateGroup = rescueList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s",DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
//			}
//			result.forEach(e -> dateGroup.forEach((k, v) -> {
//				long num = 0;
//				if(e.getType().equals(labelArr[0])){
//					num = v.stream().mapToLong(SugarRescueListVO::getWorkMill).max().orElse(0);
//				}else if(e.getType().equals(labelArr[1])) {
//					long planTotal = v.stream().count();
//					long sum = v.stream().mapToLong(SugarRescueListVO::getWorkMill).sum();
//					num = BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(planTotal),1,RoundingMode.HALF_UP).longValue();
//				}else if(e.getType().equals(labelArr[2])) {
//					num = v.stream().mapToLong(SugarRescueListVO::getWorkMill).min().orElse(0);
//				}
//				e.getTrendData().put(k,BigDecimal.valueOf(num).divide(BigDecimal.valueOf(60000),1,RoundingMode.HALF_UP));
//			}));
//		}
//
//		searchDTO.setStartDate(startCrrDate);
//		searchDTO.setEndDate(endCrrDate);
//		List<SugarRescueListVO> rescueCrrList = baseMapper.selectSugarRescueList(searchDTO);
//		if(ObjectUtil.isNotEmpty(rescueCrrList)){
//			Map<String,List<SugarRescueListVO>> dateCrrGroup;
//			if(searchDTO.getDataType().equals(1)){
//				dateCrrGroup = rescueCrrList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(),"yyyy/M/d")));
//			}else {
//				dateCrrGroup = rescueCrrList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s",DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
//			}
//			// 获取平均值结果
//			result.forEach(e -> {
//				if(e.getType().equals(labelArr[3])){
//					StatisticsTrendVO trendVO = result.get(1);
//					e.getTrendData().forEach((k,v) -> {
//						String kCrr;
//						if(searchDTO.getDataType().equals(1)){
//							kCrr = DateUtil.format(DateUtil.offsetDay(DateUtil.parseDate(k),-1),"yyyy/M/d");
//						}else {
//							String[] dateArr = k.split("~");
//							Date startCrrKeyDate = DateUtil.parseDate(dateArr[0]);
//							Date endCrrKeyDate = DateUtil.parseDate(dateArr[1]);
//							kCrr = String.format("%s~%s",DateUtil.format(DateUtil.offsetDay(startCrrKeyDate,-7), "yyyy/M/d"), DateUtil.format(DateUtil.offsetDay(endCrrKeyDate,-7), "yyyy/M/d"));
//						}
//						double num = Double.parseDouble(String.valueOf(trendVO.getTrendData().get(k)));
//						if(dateCrrGroup.containsKey(kCrr)){
//							long planTotalCrr = dateCrrGroup.get(kCrr).stream().count();
//							long sumCrr = dateCrrGroup.get(kCrr).stream().mapToLong(SugarRescueListVO::getWorkMill).sum();
//							long numCrr = BigDecimal.valueOf(sumCrr).divide(BigDecimal.valueOf(planTotalCrr).multiply(BigDecimal.valueOf(60000)),3,RoundingMode.HALF_UP).longValue();
//							if(numCrr > 0){
//								if(num > 0){
//									e.getTrendData().put(k,BigDecimal.valueOf(num).subtract(BigDecimal.valueOf(numCrr)).divide(BigDecimal.valueOf(numCrr),3,RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
//								}else {
//									e.getTrendData().put(k,BigDecimal.valueOf(numCrr).multiply(BigDecimal.valueOf(-100)));
//								}
//							}else {
//								e.getTrendData().put(k,BigDecimal.valueOf(num).multiply(BigDecimal.valueOf(100)));
//							}
//						}else {
//							e.getTrendData().put(k,BigDecimal.valueOf(num).multiply(BigDecimal.valueOf(100)));
//						}
//					});
//				}
//			});
//		}

		return resultMap;
	}


	/**
	 * 初始化救援趋势数据
	 *
	 * @param searchDTO
	 * @param initType
	 * @return
	 */
	private StatisticsTrendVO initTrendEntirety(SugarRescueSearchDTO searchDTO, String initType) {
		List<StatisticsTrendVO> initList = new ArrayList<>();
		StatisticsTrendVO init = new StatisticsTrendVO();
		init.setType(initType);
		Map<String, Object> trendData = new LinkedHashMap<>(32);
		if (searchDTO.getDataType().equals(1)) {
			// 日
			List<DateTime> rangeDate = DateUtil.rangeToList(searchDTO.getStartDate(), searchDTO.getEndDate(), DateField.DAY_OF_YEAR);
			rangeDate.forEach(date -> {
				trendData.put(DateUtil.format(date, "yyyy/M/d"), BigDecimal.ZERO);
			});
		} else if (searchDTO.getDataType().equals(2)) {
			// 周
			List<DateTime> rangeDate = DateUtil.rangeToList(searchDTO.getStartDate(), searchDTO.getEndDate(), DateField.DAY_OF_YEAR);
			rangeDate.forEach(date -> {
				String weekDate = String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(date), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(date), "yyyy/M/d"));
				if (!trendData.containsKey(weekDate)) {
					trendData.put(weekDate, BigDecimal.ZERO);
				}
			});
		} else {
			// 月
			List<DateTime> rangeDate = DateUtil.rangeToList(searchDTO.getStartDate(), searchDTO.getEndDate(), DateField.MONTH);
			rangeDate.forEach(date -> {
				String monthDate = String.format("%s月", DateUtil.format(date, "yyyy年M"));
				trendData.put(monthDate, BigDecimal.ZERO);
			});
		}
		init.setTrendData(trendData);
		return init;
	}

	/**
	 * 初始化救援趋势整体数据
	 *
	 * @param searchDTO
	 * @param initTypeArr
	 * @return
	 */
	private List<StatisticsTrendVO> initTrendEntirety(SugarRescueSearchDTO searchDTO, String[] initTypeArr) {
		List<StatisticsTrendVO> initList = new ArrayList<>();
		for (String initType : initTypeArr) {
			StatisticsTrendVO init = new StatisticsTrendVO();
			init.setType(initType);
			Map<String, Object> trendData = new LinkedHashMap<>(32);
			if (searchDTO.getDataType().equals(1)) {
				// 日
				List<DateTime> rangeDate = DateUtil.rangeToList(searchDTO.getStartDate(), searchDTO.getEndDate(), DateField.DAY_OF_YEAR);
				rangeDate.forEach(date -> {
					trendData.put(DateUtil.format(date, "yyyy/M/d"), BigDecimal.ZERO);
				});
			} else if (searchDTO.getDataType().equals(2)) {
				// 周
				List<DateTime> rangeDate = DateUtil.rangeToList(searchDTO.getStartDate(), searchDTO.getEndDate(), DateField.DAY_OF_YEAR);
				rangeDate.forEach(date -> {
					String weekDate = String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(date), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(date), "yyyy/M/d"));
					if (!trendData.containsKey(weekDate)) {
						trendData.put(weekDate, BigDecimal.ZERO);
					}
				});
			} else {
				// 月
				List<DateTime> rangeDate = DateUtil.rangeToList(searchDTO.getStartDate(), searchDTO.getEndDate(), DateField.MONTH);
				rangeDate.forEach(date -> {
					String monthDate = String.format("%s月", DateUtil.format(date, "yyyy年M"));
					trendData.put(monthDate, BigDecimal.ZERO);
				});
			}
			init.setTrendData(trendData);

			initList.add(init);
		}
		return initList;
	}

	private Map<Integer, Long> initRescueHighTime() {
		Map<Integer, Long> resultMap = new LinkedHashMap<>(24);
		for (int i = 0; i < 24; i++) {
			resultMap.put(i, 0L);
		}
		return resultMap;
	}

	public static void main(String[] args) {
		System.err.println(DateUtil.secondToTime(3601));
	}

	@Override
	public Map<String, String> countByResources(SugarRescueSearchDTO searchDTO) {

		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
		List<RescueCountByResourceVO> resultList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			resultList = baseMapper.countByResource(searchDTO.getStartDate(), searchDTO.getEndDate());
			resultList = resultList.stream().filter(e -> elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
		}
		// 2023-09-27 新增每次次数显示
		String phoneNum = "phoneNum";
		String repairNum = "repairNum";
		String networkNum = "networkNum";
		String estateNum = "estateNum";
		String groupNum = "groupNum";
		String buttonNum = "buttonNum";

		if (resultList.size() == 0 || Func.isEmpty(resultList)) {
			Map<String, String> zero = new HashMap<>(10);
			zero.put("phone", "0%");
			zero.put("repair", "0%");
			zero.put("network", "0%");
			zero.put("estate", "0%");
			zero.put("group", "0%");
			zero.put("button", "0%");
			zero.put(phoneNum, "0");
			zero.put(repairNum, "0");
			zero.put(networkNum, "0");
			zero.put(estateNum, "0");
			zero.put(groupNum, "0");
			zero.put(buttonNum, "0");
			return zero;
		}
		int phone = 0;
		int repair = 0;
		int network = 0;
		int estate = 0;
		int group = 0;
		int button = 0;
		for (RescueCountByResourceVO r : resultList) {
			if ("1_1".equals(r.getDictKey())) {
				phone++;
			} else if ("1_2".equals(r.getDictKey())) {
				repair++;
			} else if ("1_3".equals(r.getDictKey())) {
				network++;
			} else if ("1_4".equals(r.getDictKey())) {
				estate++;
			} else if ("1_5".equals(r.getDictKey())) {
				group++;
			} else if ("1_6".equals(r.getDictKey())) {
				button++;
			}
		}
		BigDecimal size = new BigDecimal(resultList.size());
		BigDecimal p = new BigDecimal(phone);
		BigDecimal r = new BigDecimal(repair);
		BigDecimal n = new BigDecimal(network);
		BigDecimal e = new BigDecimal(estate);
		BigDecimal g = new BigDecimal(group);
		BigDecimal b = new BigDecimal(button);

		BigDecimal fromPhone = p.multiply(new BigDecimal(100)).divide(size, 5, RoundingMode.HALF_EVEN);
		BigDecimal fromRepair = r.multiply(new BigDecimal(100)).divide(size, 5, RoundingMode.HALF_EVEN);
		BigDecimal fromNetwork = n.multiply(new BigDecimal(100)).divide(size, 5, RoundingMode.HALF_EVEN);
		BigDecimal fromEstate = e.multiply(new BigDecimal(100)).divide(size, 5, RoundingMode.HALF_EVEN);
		BigDecimal fromGroup = g.multiply(new BigDecimal(100)).divide(size, 5, RoundingMode.HALF_EVEN);
		BigDecimal fromButton = b.multiply(new BigDecimal(100)).divide(size, 5, RoundingMode.HALF_EVEN);
		Map<String, String> res = new HashMap<>(10);
		res.put("phone", fromPhone.setScale(1, RoundingMode.HALF_EVEN) + "%");
		res.put("repair", fromRepair.setScale(1, RoundingMode.HALF_EVEN) + "%");
		res.put("network", fromNetwork.setScale(1, RoundingMode.HALF_EVEN) + "%");
		res.put("estate", fromEstate.setScale(1, RoundingMode.HALF_EVEN) + "%");
		res.put("group", fromGroup.setScale(1, RoundingMode.HALF_EVEN) + "%");
		res.put("button", fromButton.setScale(1, RoundingMode.HALF_EVEN) + "%");
		res.put(phoneNum, p.toString());
		res.put(repairNum, r.toString());
		res.put(networkNum, n.toString());
		res.put(estateNum, e.toString());
		res.put(groupNum, g.toString());
		res.put(buttonNum, b.toString());
		return res;
	}

	@Override
	public Object init() {
		List<SugarRescueListVO> rescueCrrList = baseMapper.selectSugarRescueListAll();
		List<MaintainPlanContentExt> planContentExts = new ArrayList<>();
		rescueCrrList.forEach(e -> {
			MaintainPlanContentExt planContentExt = maintainPlanContentExtService.getOne(Wrappers.<MaintainPlanContentExt>lambdaQuery().eq(MaintainPlanContentExt::getPlanId, e.getPlanId()));
			planContentExt.setAllocateTime(e.getUserCreateTime());
			planContentExt.setAllocateDuration(e.getUserCreateTime().getTime() - e.getCreateTime().getTime());
			planContentExt.setArriveDuration(e.getArriveTime().getTime() - e.getCreateTime().getTime());
			planContentExt.setWorkMill(e.getPlanEndTime().getTime() - e.getPlanBeginTime().getTime());
			if (planContentExt.getArriveDuration() > 30 * 60 * 1000) {
				planContentExt.setIsOvertime(1);
			} else {
				planContentExt.setIsOvertime(3);
				if (e.getRescueTime() == 1) {
					int hour = DateUtil.hour(e.getCreateTime(), Boolean.TRUE);
					if (6 <= hour && hour < 18) {
						if (planContentExt.getArriveDuration() > e.getRescueDaytime()) {
							planContentExt.setIsOvertime(2);
						}
					} else {
						if (planContentExt.getArriveDuration() > e.getRescueNight()) {
							planContentExt.setIsOvertime(2);
						}
					}
				}
			}
			planContentExts.add(planContentExt);
		});
		return maintainPlanContentExtService.updateBatchById(planContentExts);
	}

	@Override
	public MaintainPlanContent getMaintainPlanById(Long planId) {
		return maintainPlanContentService.selectById(planId);
	}

	@Override
	public StatisticsTrendVO selectRescueTiringPro(SugarRescueSearchDTO searchDTO) {
		Date startDate = searchDTO.getStartDate();
		Date endDate = searchDTO.getEndDate();
		if (searchDTO.getDataType().equals(2)) {
			startDate = DateUtil.beginOfWeek(startDate);
			endDate = DateUtil.endOfWeek(endDate);
		} else if (searchDTO.getDataType().equals(3)) {
			startDate = DateUtil.beginOfMonth(startDate);
			endDate = DateUtil.endOfMonth(endDate);
		}
		searchDTO.setStartDate(startDate);
		searchDTO.setEndDate(endDate);

		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());

		List<SugarRescueListVO> rescueList = new ArrayList<>();
		List<ElevatorMaintainStatusDay> elevatorMaintainStatusDays = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueList = baseMapper.selectSugarRescueList(searchDTO);
			rescueList = rescueList.stream().filter(e -> elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
			elevatorMaintainStatusDays = elevatorMaintainStatusDayService.listByRecordDate(searchDTO.getStartDate(), searchDTO.getEndDate());
			elevatorMaintainStatusDays = elevatorMaintainStatusDays.stream().filter(e -> elevatorIds.contains(e.getElevatorId()) && (
				e.getMaintainStatus().equals(ElevatorMaintainStatusEnums.CONTRACT_IN_TIME.getId())
					|| e.getMaintainStatus().equals(ElevatorMaintainStatusEnums.CONTRACT_OUT_TIME.getId())
					|| e.getMaintainStatus().equals(ElevatorMaintainStatusEnums.OUT_TIME_STOP.getId())
					|| e.getMaintainStatus().equals(ElevatorMaintainStatusEnums.OUT_TIME_END.getId())
					|| e.getMaintainStatus().equals(ElevatorMaintainStatusEnums.OUT_TIME_END_HAS_NEXT.getId())
					|| e.getMaintainStatus().equals(ElevatorMaintainStatusEnums.DIS_ENABLE.getId())
			)).collect(Collectors.toList());
		}
		StatisticsTrendVO result = this.initTrendEntirety(searchDTO, "0.00");
		if (ObjectUtil.isNotEmpty(rescueList) && ObjectUtil.isNotEmpty(elevatorMaintainStatusDays)) {
			long allElevatorCount = elevatorMaintainStatusDays.stream().map(ElevatorMaintainStatusDay::getElevatorId).distinct().count();
			if (allElevatorCount > 0) {
				long month = DateUtil.betweenMonth(startDate, endDate, Boolean.FALSE) + 1;
				String tiringProLabel = BigDecimal.valueOf(rescueList.size()).divide(BigDecimal.valueOf(allElevatorCount), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).divide(BigDecimal.valueOf(month), 2, RoundingMode.HALF_UP).toString();
				result = this.initTrendEntirety(searchDTO, tiringProLabel);
			}
			Map<String, List<SugarRescueListVO>> dateGroup;
			Map<String, List<ElevatorMaintainStatusDay>> maintainDateGroup;
			if (searchDTO.getDataType().equals(1)) {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getPlanDate(), "yyyy/M/d")));
				maintainDateGroup = elevatorMaintainStatusDays.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getRecordDate(), "yyyy/M/d")));
			} else if (searchDTO.getDataType().equals(2)) {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getPlanDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getPlanDate()), "yyyy/M/d"))));
				maintainDateGroup = elevatorMaintainStatusDays.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getRecordDate()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getRecordDate()), "yyyy/M/d"))));
			} else {
				dateGroup = rescueList.stream().collect(Collectors.groupingBy(e -> String.format("%s月", DateUtil.format(e.getPlanDate(), "yyyy年M"))));
				maintainDateGroup = elevatorMaintainStatusDays.stream().collect(Collectors.groupingBy(e -> String.format("%s月", DateUtil.format(e.getRecordDate(), "yyyy年M"))));
			}
			StatisticsTrendVO finalResult = result;
			finalResult.getTrendData().keySet().forEach(e -> {
				long planCount = 0;
				if (dateGroup.containsKey(e)) {
					planCount = dateGroup.get(e).stream().count();
				}
				long elevatorCount = 0;
				if (maintainDateGroup.containsKey(e)) {
					elevatorCount = maintainDateGroup.get(e).stream().map(ElevatorMaintainStatusDay::getElevatorId).distinct().count();
				}
				if (planCount > 0 && elevatorCount > 0) {
					finalResult.getTrendData().put(e, BigDecimal.valueOf(planCount).divide(BigDecimal.valueOf(elevatorCount), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
				}
			});
			return finalResult;
		}
		return result;
	}

	@Override
	public List<Map<String, Object>> selectArriveDurationOvertimeList(SugarRescueSearchDTO searchDTO) {

		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
		List<Map<String, Object>> result = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			result = baseMapper.selectArriveDurationOvertimeList(searchDTO);
			result = result.stream().filter(e -> elevatorIds.contains(Long.valueOf(String.valueOf(e.get("elevatorId"))))).collect(Collectors.toList());
		}
		if (Func.isNotEmpty(result)) {
			result.forEach(e -> {
				e.put("isOvertimeName", Integer.parseInt(String.valueOf(e.get("isOvertime"))) == 1 ? "超法规时长" : "超项目指定时长");
				e.put("arriveDurationMin", DateUtil.secondToTime(BigDecimal.valueOf(Long.parseLong(String.valueOf(e.get("arriveDuration")))).divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP).intValue()));
			});
		}
		return result;
	}

	@Override
	public Map<String, Object> selectRescueTiringProDetail(SugarRescueSearchDTO searchDTO) {
		Map<String, Object> resultMap = new HashMap<>();
		resultMap.put("label", "0.00");

		List<Long> deptIds = deptService.selectDeptAndAllChildIds(searchDTO.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());

		List<SugarRescueListVO> rescueList = new ArrayList<>();
		List<ElevatorMaintainStatusDay> elevatorMaintainStatusDays = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			rescueList = baseMapper.selectSugarRescueList(searchDTO);
			rescueList = rescueList.stream().filter(e -> elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
			elevatorMaintainStatusDays = elevatorMaintainStatusDayService.listByRecordDate(searchDTO.getStartDate(), searchDTO.getEndDate());
			elevatorMaintainStatusDays = elevatorMaintainStatusDays.stream().filter(e -> elevatorIds.contains(e.getElevatorId()) && (
				e.getMaintainStatus().equals(ElevatorMaintainStatusEnums.CONTRACT_IN_TIME.getId())
					|| e.getMaintainStatus().equals(ElevatorMaintainStatusEnums.CONTRACT_OUT_TIME.getId())
					|| e.getMaintainStatus().equals(ElevatorMaintainStatusEnums.OUT_TIME_STOP.getId())
					|| e.getMaintainStatus().equals(ElevatorMaintainStatusEnums.OUT_TIME_END.getId())
					|| e.getMaintainStatus().equals(ElevatorMaintainStatusEnums.OUT_TIME_END_HAS_NEXT.getId())
					|| e.getMaintainStatus().equals(ElevatorMaintainStatusEnums.DIS_ENABLE.getId())
			)).collect(Collectors.toList());
		}
		if (ObjectUtil.isNotEmpty(rescueList) && ObjectUtil.isNotEmpty(elevatorMaintainStatusDays)) {
			long allElevatorCount = elevatorMaintainStatusDays.stream().map(ElevatorMaintainStatusDay::getElevatorId).distinct().count();
			if (allElevatorCount > 0) {
				String tiringProLabel = BigDecimal.valueOf(rescueList.size()).divide(BigDecimal.valueOf(allElevatorCount), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toString();
				resultMap.put("label", tiringProLabel);
			}
		}
		return resultMap;
	}

	@Override
	public MapRescuePlanDetailVO planRescueDetailList(Long buildingId, Long elevatorId) {
		MapRescuePlanDetailVO mapRescuePlanDetailVO = baseMapper.planRescueDetailListByBuildingId(buildingId, elevatorId);
		if (mapRescuePlanDetailVO != null) {
			List<MapRescuePlanDetailListVO> planDetailList = mapRescuePlanDetailVO.getPlanDetailList();
			List<Long> elevatorIds = planDetailList.stream().map(t -> t.getElevatorId()).collect(Collectors.toList());
			// 查询电梯设备状态
			Map<Long, DeviceStatusVO> deviceStatusMap = estateTlwScreenService.getDeviceStatus(elevatorIds);
			for (MapRescuePlanDetailListVO planDetail : planDetailList) {
				if (!deviceStatusMap.containsKey(planDetail.getElevatorId())) {
					continue;
				}
				DeviceStatusVO deviceStatus = this.getVideoDeviceStatus(deviceStatusMap.get(planDetail.getElevatorId()));
				planDetail.setDeviceId(deviceStatus.getDeviceId());
				planDetail.setDeviceStatus(deviceStatus.getDeviceStatus());
			}
		}

		return mapRescuePlanDetailVO;
	}

	@Override
	public DeviceStatusVO getElevatorDeviceStatus(Long elevatorId) {
		// 查询电梯设备状态
		Map<Long, DeviceStatusVO> deviceStatusMap = estateTlwScreenService.getDeviceStatus(Arrays.asList(elevatorId));
		return getVideoDeviceStatus(deviceStatusMap.get(elevatorId));
	}

	@Override
	public PlanRescueDetailVO getRescueDetail(Long planId) {
		PlanRescueDetailVO planRescueDetail = baseMapper.getRescueDetail(planId);
		if (planRescueDetail == null) {
			throw new ServiceException("未找到相关数据!");
		}
		// 查询电梯设备状态
		DeviceStatusVO deviceStatusVO = this.getElevatorDeviceStatus(planRescueDetail.getElevatorId());
		planRescueDetail.setDeviceStatus(deviceStatusVO == null ? -1 : deviceStatusVO.getDeviceStatus());
		// 查询项目要求救援到达时间
		BuildingRescueTimeVO rescueTimeVO = buildingExtService.getBuildingRescueTime(planRescueDetail.getBuildingId(), planRescueDetail.getCreateTime());
		planRescueDetail.setRescueUseTime(rescueTimeVO.getRescueUseTime());
		return planRescueDetail;
	}

	@Override
	public Map<String, Object> getMaintainBuildingElevatorCount() {
		Map<String, Object> resultMap = baseMapper.getMaintainBuildingElevatorCount();
		return resultMap;
	}

	@Override
	public Map<String, Object> getPlanResultCount() {
		Map<String, Object> resultMap = new HashMap<>();
		resultMap.put("isOvertime", 0);
		resultMap.put("isOvertimePro", 0);
		List<MaintainPlanContentExt> planContentExts = baseMapper.getRescueTodayFinishPlan();
		if (Func.isNotEmpty(planContentExts)) {
			resultMap.put("isOvertime", planContentExts.size());
			long isOvertimePro = planContentExts.stream().filter(e -> e.getIsOvertime().equals(3)).count();
			resultMap.put("isOvertimePro", BigDecimal.valueOf(isOvertimePro).divide(BigDecimal.valueOf(planContentExts.size()), 3, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP));
		}
		return resultMap;
	}

	@Override
	public List<Long> selectRescueSugarUser(String tenantId) {
		return baseMapper.selectRescueSugarUser(tenantId);
	}

	@Override
	public RescueRealTimeCountVO selectRealTimeCount() {
		return baseMapper.selectRealTimeCount();
	}

	@Override
	public List<RescueRealTimePlanListVO> selectRealTimePlanList(Integer planStatus, Integer arrivalWarning) {
		List<RescueRealTimePlanListVO> resultList = baseMapper.selectRealTimePlanList(planStatus, arrivalWarning);
		if (Func.isNotEmpty(resultList)) {
			resultList.forEach(e -> {
				if (e.getPlanStatus().equals(TaskStatusEnum.JY_ARRIVE.getStatus())) {
					e.setBetweenMin(DateUtil.between(e.getCreateTime(), e.getArriveTime(), DateUnit.MINUTE));
				} else {
					e.setBetweenMin(DateUtil.between(e.getCreateTime(), DateUtil.date(), DateUnit.MINUTE));
				}
			});
			List<Long> elevatorIds = resultList.stream().map(RescueRealTimePlanListVO::getElevatorId).filter(elevatorId -> elevatorId != 0).collect(Collectors.toList());
			if (Func.isNotEmpty(elevatorIds)) {
				// 查询电梯设备状态
				Map<Long, DeviceStatusVO> deviceStatusMap = estateTlwScreenService.getDeviceStatus(elevatorIds);
				for (RescueRealTimePlanListVO planDetail : resultList) {
					if (!deviceStatusMap.containsKey(planDetail.getElevatorId())) {
						continue;
					}
					DeviceStatusVO deviceStatus = this.getVideoDeviceStatus(deviceStatusMap.get(planDetail.getElevatorId()));
					planDetail.setDeviceId(deviceStatus.getDeviceId());
					planDetail.setDeviceStatus(deviceStatus.getDeviceStatus());
				}
			}
		}
		return resultList;
	}

	@Override
	public List<RescueRealTimeListVO> selectRealTimeBuildingList() {
		List<RescueRealTimeListVO> resultList = baseMapper.selectRealTimeBuildingList();
		if (Func.isNotEmpty(resultList)) {
			resultList.forEach(e -> {
				if (Func.isNotEmpty(e.getPlanList())) {
					e.getPlanList().forEach(j -> {
						if (j.getPlanStatus().equals(TaskStatusEnum.JY_ARRIVE.getStatus())) {
							j.setBetweenMin(DateUtil.between(j.getCreateTime(), j.getArriveTime(), DateUnit.MINUTE));
						} else {
							j.setBetweenMin(DateUtil.between(j.getCreateTime(), DateUtil.date(), DateUnit.MINUTE));
						}
					});
					List<Long> elevatorIds = e.getPlanList().stream().map(RescueRealTimePlanListVO::getElevatorId).filter(elevatorId -> elevatorId != 0).collect(Collectors.toList());
					if (Func.isNotEmpty(elevatorIds)) {
						// 查询电梯设备状态
						Map<Long, DeviceStatusVO> deviceStatusMap = estateTlwScreenService.getDeviceStatus(elevatorIds);
						for (RescueRealTimePlanListVO planDetail : e.getPlanList()) {
							if (!deviceStatusMap.containsKey(planDetail.getElevatorId())) {
								continue;
							}
							DeviceStatusVO deviceStatus = this.getVideoDeviceStatus(deviceStatusMap.get(planDetail.getElevatorId()));
							planDetail.setDeviceId(deviceStatus.getDeviceId());
							planDetail.setDeviceStatus(deviceStatus.getDeviceStatus());
						}
					}
					e.setPlanTotal(e.getPlanList().size());
				} else {
					e.setPlanTotal(0);
				}
			});
			resultList = resultList.stream().sorted(Comparator.comparing(RescueRealTimeListVO::getPlanTotal).reversed()).collect(Collectors.toList());
		}
		return resultList;
	}

	@Override
	public List<RescueRealTimeListVO> selectRealTimeUserList() {
		List<RescueRealTimeListVO> resultList = baseMapper.selectRealTimeUserList();
		if (Func.isNotEmpty(resultList)) {
			resultList.forEach(e -> {
				if (Func.isNotEmpty(e.getPlanList())) {
					e.getPlanList().forEach(j -> {
						if (j.getPlanStatus().equals(TaskStatusEnum.JY_ARRIVE.getStatus())) {
							j.setBetweenMin(DateUtil.between(j.getCreateTime(), j.getArriveTime(), DateUnit.MINUTE));
						} else {
							j.setBetweenMin(DateUtil.between(j.getCreateTime(), DateUtil.date(), DateUnit.MINUTE));
						}
					});
					List<Long> elevatorIds = e.getPlanList().stream().map(RescueRealTimePlanListVO::getElevatorId).filter(elevatorId -> elevatorId != 0).collect(Collectors.toList());
					if (Func.isNotEmpty(elevatorIds)) {
						// 查询电梯设备状态
						Map<Long, DeviceStatusVO> deviceStatusMap = estateTlwScreenService.getDeviceStatus(elevatorIds);
						for (RescueRealTimePlanListVO planDetail : e.getPlanList()) {
							if (!deviceStatusMap.containsKey(planDetail.getElevatorId())) {
								continue;
							}
							DeviceStatusVO deviceStatus = this.getVideoDeviceStatus(deviceStatusMap.get(planDetail.getElevatorId()));
							planDetail.setDeviceId(deviceStatus.getDeviceId());
							planDetail.setDeviceStatus(deviceStatus.getDeviceStatus());
						}
					}
					e.setPlanTotal(e.getPlanList().size());
				} else {
					e.setPlanTotal(0);
				}
			});
			resultList = resultList.stream().sorted(Comparator.comparing(RescueRealTimeListVO::getPlanTotal).reversed()).collect(Collectors.toList());
		}
		return resultList;
	}

	@Override
	public List<MapUserInfoCardDetailPlanListVO> mapUserScreenDetailPlanList(Long userId, Integer typeStatus) {
		List<MapUserInfoCardDetailPlanListVO> resultList = baseMapper.mapUserScreenDetailPlanList(userId, typeStatus);
		if (Func.isNotEmpty(resultList)) {
			resultList.forEach(e -> e.setPlanStatusName(TaskStatusEnum.getTaskName(e.getTypeStatus(), e.getPlanStatus())));
		}
		return resultList;
	}

	@Override
	public List<UserInfoVo> getUserListV2(PlanAddUserSearchDTO planAddUserSearchDTO) {
		//表示值班人员
		Integer signType = 1;
		if (planAddUserSearchDTO.getSignType().equals(signType)) {
			List<ElevatorPrincipalDept> list = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().eq(ElevatorPrincipalDept::getElevatorId, planAddUserSearchDTO.getElevatorId()));
			MaintainSignCalendar maintainSignCalendar = maintainSignCalendarService.getOne(Wrappers.<MaintainSignCalendar>lambdaQuery().eq(MaintainSignCalendar::getDate, DateUtil.beginOfDay(new Date())));
			if (list.size() > 0) {
				List<Long> groupIds = list.stream().map(ElevatorPrincipalDept::getDeptId).distinct().collect(Collectors.toList());
				planAddUserSearchDTO.setGroupIds(groupIds);
			}
			planAddUserSearchDTO.setHoliday(maintainSignCalendar.getHoliday());
		}
		planAddUserSearchDTO.setTenantId(AuthUtil.getTenantId());
		List<UserInfoVo> list = baseMapper.getUserListV2(planAddUserSearchDTO);
		List<Building> buildingList = buildingService.listByIds(Arrays.asList(planAddUserSearchDTO.getBuildingIds().split(",")));
		List<MaintainPlanContent> planList = baseMapper.planListByUserIds(null);
		if (planList.size() > 0) {
			Map<Long, List<MaintainPlanContent>> map1 = planList.stream().collect(Collectors.groupingBy(MaintainPlanContent::getUserId));
			List<MaintainPlanContent> signList = baseMapper.getsignListByPlanIds(null);
			Map<Long, List<MaintainPlanContent>> map2 = signList.stream().collect(Collectors.groupingBy(MaintainPlanContent::getUserId));
			for (UserInfoVo userInfoVo : list) {
				List<MaintainPlanContent> maintainPlanContentList1 = map1.get(userInfoVo.getUserId());
				List<MaintainPlanContent> maintainPlanContentList2 = map2.get(userInfoVo.getUserId());
				if (maintainPlanContentList1 != null && maintainPlanContentList1.size() > 0) {
					userInfoVo.setPlanCounts(maintainPlanContentList1.size());
					List<MaintainPlanContent> planContents = maintainPlanContentList1.stream().filter(o -> o.getTypeStatus() == MaintainPlanContentEnum.MAINTAIN_KR.getId() && (o.getStatus() == TaskStatusEnum.JY_PROCESSED.getStatus() || o.getStatus() == TaskStatusEnum.JY_ARRIVE.getStatus())).collect(Collectors.toList());
					List<Integer> typeStatus = new ArrayList<>(1);
					if (planContents != null && planContents.size() > 0) {
						typeStatus.add(3);
						userInfoVo.setTypeStatus(typeStatus);
					}
				}
				if (maintainPlanContentList2 != null && maintainPlanContentList2.size() > 0) {
					List<Integer> typeStatus = maintainPlanContentList2.stream().map(MaintainPlanContent::getTypeStatus).distinct().collect(Collectors.toList());
					if (userInfoVo.getTypeStatus() != null && userInfoVo.getTypeStatus().size() > 0) {
						typeStatus.addAll(userInfoVo.getTypeStatus());
					}
					userInfoVo.setTypeStatus(typeStatus);
				}

			}
		}

		//计算距离
		List<UserInfoVo> list3 = new ArrayList<>(16);
		for (Building building : buildingList) {
			for (UserInfoVo userInfoVo : list) {
				if (userInfoVo.getTypeStatus() == null || userInfoVo.getTypeStatus().size() == 0) {
					List<Integer> typeStatus = new ArrayList<>();
					typeStatus.add(-1);
					userInfoVo.setTypeStatus(typeStatus);
				} else {
					userInfoVo.setTypeStatus(userInfoVo.getTypeStatus().stream().distinct().collect(Collectors.toList()));
				}
				if (StrUtil.isNotEmpty(userInfoVo.getLatitude()) && StrUtil.isNotEmpty(userInfoVo.getLongitude())) {
					Double distance = MapUtil.getDistanceToDouble(Convert.toDouble(userInfoVo.getLongitude()), Convert.toDouble(userInfoVo.getLatitude()), Convert.toDouble(building.getLongitude()), Convert.toDouble(building.getLatitude()));
					userInfoVo.setDistance(distance);
				} else {
					userInfoVo.setDistance(900000001.0);
				}

				if (StrUtil.isNotEmpty(userInfoVo.getUserLatitude()) && StrUtil.isNotEmpty(userInfoVo.getUserLongitude())) {
					Double distance = MapUtil.getDistanceToDouble(Convert.toDouble(userInfoVo.getUserLongitude()), Convert.toDouble(userInfoVo.getUserLatitude()), Convert.toDouble(building.getLongitude()), Convert.toDouble(building.getLatitude()));
					userInfoVo.setUserDistance(distance);
				} else {
					userInfoVo.setUserDistance(900000001.0);
				}

				//计算距离实时定位
				if (planAddUserSearchDTO.getDistanceType() != null && planAddUserSearchDTO.getDistanceType() == 1) {
					if (userInfoVo.getDistance() < Convert.toDouble(planAddUserSearchDTO.getDistance(), 900000001.0)) {
						List<Long> userIds = list3.stream().map(UserInfoVo::getUserId).distinct().collect(Collectors.toList());
						if (!userIds.contains(userInfoVo.getUserId())) {
							list3.add(userInfoVo);
						}
					}
				}
				//计算距离实时定位
				if (planAddUserSearchDTO.getDistanceType() != null && planAddUserSearchDTO.getDistanceType() == 2) {
					if (userInfoVo.getUserDistance() < Convert.toDouble(planAddUserSearchDTO.getDistance(), 900000001.0)) {
						List<Long> userIds = list3.stream().map(UserInfoVo::getUserId).distinct().collect(Collectors.toList());
						if (!userIds.contains(userInfoVo.getUserId())) {
							list3.add(userInfoVo);
						}
					}
				}
			}
		}
		//计算距离实时定位
		if (planAddUserSearchDTO.getDistance() != null && planAddUserSearchDTO.getDistance() > 0) {
			list = list3;
		}


		//闲忙情况筛选
		List<UserInfoVo> list2 = new ArrayList<>(16);
		if (planAddUserSearchDTO.getTypeStatus() != null && planAddUserSearchDTO.getTypeStatus().size() > 0) {
			for (UserInfoVo userInfoVo : list) {
				List<Integer> typeStatus = userInfoVo.getTypeStatus();
				for (Integer type : typeStatus) {
					if (planAddUserSearchDTO.getTypeStatus().contains(type)) {
						List<Long> userIds = list2.stream().map(UserInfoVo::getUserId).distinct().collect(Collectors.toList());
						if (!userIds.contains(userInfoVo.getUserId())) {
							list2.add(userInfoVo);
						}
					}
				}
			}
			list = list2;
		}
		if (Convert.toInt(planAddUserSearchDTO.getDistanceType(), 1) == 1) {
			list = list.stream().sorted(Comparator.comparingDouble(UserInfoVo::getDistance)).collect(Collectors.toList());
		} else {
			list = list.stream().sorted(Comparator.comparingDouble(UserInfoVo::getUserDistance)).collect(Collectors.toList());
		}
		return list;
	}

	@Override
	public UserTodayPlanDetailVo userPlanInfo(Long userId) {
		UserTodayPlanDetailVo userTodayPlanDetailVo = new UserTodayPlanDetailVo();
		List<MaintainPlanContent> planList = baseMapper.getTodayUserPlanInfo(userId);
		List<MaintainPlanContent> byList = planList.stream().filter(o -> o.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_BY.getId())).collect(Collectors.toList());
		List<MaintainPlanContent> wxList = planList.stream().filter(o -> o.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_WX.getId())).collect(Collectors.toList());
		List<MaintainPlanContent> krList = planList.stream().filter(o -> o.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_KR.getId())).collect(Collectors.toList());
		List<MaintainPlanContent> pjList = planList.stream().filter(o -> o.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_PARTS.getId())).collect(Collectors.toList());
		List<MaintainPlanContent> swList = planList.stream().filter(o -> o.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_AFFAIR.getId())).collect(Collectors.toList());
		List<MaintainPlanContent> zjList = planList.stream().filter(o -> o.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_CHECK.getId())).collect(Collectors.toList());

		userTodayPlanDetailVo.setByPlanCounts(byList.size());
		userTodayPlanDetailVo.setByDisPlanCounts(byList.size() - byList.stream().filter(o -> o.getStatus().equals(TaskStatusEnum.BY_DISABLE.getStatus()) || o.getStatus().equals(TaskStatusEnum.BY_PROCESSED.getStatus())).collect(Collectors.toList()).size());

		userTodayPlanDetailVo.setWxPlanCounts(wxList.size());
		userTodayPlanDetailVo.setWxDisPlanCounts(wxList.size() - wxList.stream().filter(o -> o.getStatus().equals(TaskStatusEnum.WX_TO_START.getStatus()) || o.getStatus().equals(TaskStatusEnum.WX_UNDER_WAY.getStatus())).collect(Collectors.toList()).size());

		userTodayPlanDetailVo.setJyPlanCounts(krList.size());
		userTodayPlanDetailVo.setJyDisPlanCounts(krList.size() - krList.stream().filter(o -> o.getStatus().equals(TaskStatusEnum.JY_TO_START.getStatus()) || o.getStatus().equals(TaskStatusEnum.JY_PROCESSED.getStatus()) || o.getStatus().equals(TaskStatusEnum.JY_ARRIVE.getStatus())).collect(Collectors.toList()).size());


		userTodayPlanDetailVo.setPjPlanCounts(pjList.size());
		userTodayPlanDetailVo.setPjDisPlanCounts(pjList.size() - pjList.stream().filter(o -> o.getStatus().equals(TaskStatusEnum.PARTS_TO_START.getStatus()) || o.getStatus().equals(TaskStatusEnum.PARTS_CHANGE.getStatus())).collect(Collectors.toList()).size());

		userTodayPlanDetailVo.setSwPlanCounts(swList.size());
		userTodayPlanDetailVo.setSwDisPlanCounts(swList.size() - swList.stream().filter(o -> o.getStatus().equals(TaskStatusEnum.SW_TO_START.getStatus()) || o.getStatus().equals(TaskStatusEnum.SW_UNDER_WAY.getStatus())).collect(Collectors.toList()).size());

		userTodayPlanDetailVo.setZjPlanCounts(zjList.size());
		userTodayPlanDetailVo.setZjDisPlanCounts(zjList.size() - zjList.stream().filter(o -> o.getStatus().equals(TaskStatusEnum.ZJ_TO_START.getStatus()) || o.getStatus().equals(TaskStatusEnum.ZJ_UNDER_WAY.getStatus())).collect(Collectors.toList()).size());

		return userTodayPlanDetailVo;
	}

	/**
	 * 判断设备在线时，是否正在通话中
	 *
	 * @param deviceStatus
	 * @return
	 */
	private DeviceStatusVO getVideoDeviceStatus(DeviceStatusVO deviceStatus) {
		if (deviceStatus != null && deviceStatus.getDeviceStatus() == 1) {
			// 如果设备在线，判断是否正在通话中
			// 会话中列表
			List<RescueNoticeSession> sessionList = rescueNoticeSessionService.list(Wrappers.<RescueNoticeSession>lambdaQuery()
				.eq(RescueNoticeSession::getElevatorId, deviceStatus.getElevatorId())
				.isNull(RescueNoticeSession::getSessionEndTime));
			if (Func.isNotEmpty(sessionList)) {
				// 通话中状态
				deviceStatus.setDeviceStatus(2);
			}
		}
		return deviceStatus;
	}

	@Override
	public List<TodayTaskListVO> getTodayTaskList(Integer typeStatus, Long userId) {
		return baseMapper.getTodayTaskList(typeStatus, userId);
	}

	@Override
	public IPage<DispatcherPageRespVO> dispatcherPage(DispatcherPageReqVO dispatcherPageReqVO, IPage<DispatcherPageRespVO> page) {
		return baseMapper.dispatcherPage(dispatcherPageReqVO, page);
	}

	@Override
	public DispatcherStatisticsVO dispatcherStatistics(DispatcherPageReqVO dispatcherPageReqVO) {
		DispatcherStatisticsVO statisticsVO = new DispatcherStatisticsVO();
		IPage<DispatcherPageRespVO> emptyPage = new Page<>(1, -1);
		IPage<DispatcherPageRespVO> page = baseMapper.dispatcherPage(new DispatcherPageReqVO(),emptyPage);
		List<DispatcherPageRespVO> list = page.getRecords();
		if (Func.isNotEmpty(list)) {
			statisticsVO.setDispatching(list.stream().filter(o -> o.getStatus().equals(TaskStatusEnum.JY_DISPATCH.getStatus())).count());
			// 已调度状态
			Integer[] dispatchedStatus = new Integer[]{TaskStatusEnum.JY_TO_START.getStatus(), TaskStatusEnum.JY_PROCESSED.getStatus(), TaskStatusEnum.JY_ARRIVE.getStatus()};
			statisticsVO.setDispatched(list.stream().filter(o -> Arrays.asList(dispatchedStatus).contains(o.getStatus())).count());
			statisticsVO.setTotal(list.size());
		}
		return statisticsVO;
	}

	@Override
	public Boolean hasRelaMalOrder(Long id) {
		return baseMapper.hasRelaMalOrder(id);
	}

	@Override
	public Long reportMal(PlanContentSaveDto planContentSaveDto) {
		if (hasRelaMalOrder(planContentSaveDto.getFromPlanId())) {
			throw new ServiceException("已存在关联的维修工单");
		}
		return maintainPlanContentService.savePlan(planContentSaveDto);
	}

	@Override
	public List<Map<String, Object>> getBiRescuePlanDetailListV2() {
		return baseMapper.getBiRescuePlanDetailListV2();
	}

	@Override
	public List<Map<String, Object>> getBiRescuePlanCountListV2() {
		return baseMapper.getBiRescuePlanCountListV2();
	}

	@Override
	public Boolean reminder(Long planId) {
		MaintainPlanContent planContent = maintainPlanContentService.getById(planId);
		if (planContent == null) {
			throw new ServiceException("未找到对应工单");
		}

		// 获取当前日期，格式：yyyy-MM-dd
		String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
		String reminderKey = RESCUE_REMINDER_KEY.concat(StringPool.DASH).concat(String.valueOf(planId)).concat(StringPool.DASH).concat(today);

		// 检查今天的催办次数
		Long count = bladeRedis.incr(reminderKey);

		// 如果是第一次设置key，设置过期时间为当天结束（第二天凌晨）
		if (count == 1) {
			LocalDateTime tomorrow = LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
			long expireSeconds = ChronoUnit.SECONDS.between(LocalDateTime.now(), tomorrow);
			bladeRedis.expire(reminderKey, expireSeconds);
		}

		// 检查是否超过每天3次的限制
		if (count > 3) {
			throw new ServiceException("今日催办次数已达上限，请明天再试");
		}

		if (planContent.getStatus() != TaskStatusEnum.JY_RESCUE.getStatus() &&
			planContent.getStatus() != TaskStatusEnum.JY_CANCEL.getStatus()) {
			MaintainMalfunctionRescue malfunctionRescue = this.getOne(Wrappers.<MaintainMalfunctionRescue>lambdaQuery().eq(MaintainMalfunctionRescue::getPlanId, planId), false);
			if (malfunctionRescue != null && malfunctionRescue.getArrivalWarning() != null && malfunctionRescue.getArrivalWarning() > 0) {
				TriggerConditionEnum triggerConditionEnum = null;
				RescueArriveEnum rescueArriveEnum = RescueArriveEnum.getEnum(malfunctionRescue.getArrivalWarning());
				switch (rescueArriveEnum) {
					case UPCOMING:
						triggerConditionEnum = TriggerConditionEnum.CONDITION_20;
						break;
					case OVERTIME:
						triggerConditionEnum = TriggerConditionEnum.CONDITION_21;
						break;
					case OVERTIME_ARRIVE:
						triggerConditionEnum = TriggerConditionEnum.CONDITION_25;
						break;
					case OVERTIME_NOT_DISPATCH:
						triggerConditionEnum = TriggerConditionEnum.CONDITION_19;
						break;
					case OVERTIME_NOT_DEPART:
						triggerConditionEnum = TriggerConditionEnum.CONDITION_24;
						break;
					default:
						break;
				}

				if (triggerConditionEnum != null) {
					// 消息通知
					eventPublisher.publishEvent(new NoticeEvent(NoticeTaskParam.builder()
						.businessType(BusinessType.RESCUE.getCode())
						.triggerNode(triggerConditionEnum.getTriggerNode())
						.triggerCondition(triggerConditionEnum.getCode())
						.bizId(planContent.getId())
						.tenantId(planContent.getTenantId())
						.build()
					));
				}
			}
		}
		return true;
	}
}
